aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile27
-rw-r--r--NEWS72
-rw-r--r--README1
-rw-r--r--com32/Makefile4
-rw-r--r--com32/chain/Makefile11
-rw-r--r--com32/chain/chain.c171
-rw-r--r--com32/chain/chain.h19
-rw-r--r--com32/chain/common.h9
-rw-r--r--com32/chain/mangle.c244
-rw-r--r--com32/chain/mangle.h34
-rw-r--r--com32/chain/options.c230
-rw-r--r--com32/chain/options.h54
-rw-r--r--com32/chain/partiter.c619
-rw-r--r--com32/chain/partiter.h85
-rw-r--r--com32/chain/utility.c136
-rw-r--r--com32/chain/utility.h79
-rw-r--r--com32/cmenu/Makefile27
-rw-r--r--com32/cmenu/adv_menu.tpl2
-rw-r--r--com32/cmenu/complex.c2
-rw-r--r--com32/cmenu/display.c2
-rw-r--r--com32/cmenu/libmenu/cmenu.h (renamed from com32/cmenu/libmenu/menu.h)0
-rw-r--r--com32/cmenu/libmenu/help.h2
-rw-r--r--com32/cmenu/libmenu/menu.c2
-rw-r--r--com32/cmenu/libmenu/syslnx.c77
-rw-r--r--com32/cmenu/simple.c2
-rw-r--r--com32/elflink/Makefile36
-rw-r--r--com32/elflink/ldlinux/Makefile50
-rw-r--r--com32/elflink/ldlinux/adv.c (renamed from com32/lib/syslinux/adv.c)12
-rw-r--r--com32/elflink/ldlinux/advwrite.c (renamed from com32/lib/syslinux/advwrite.c)2
-rw-r--r--com32/elflink/ldlinux/chainboot.c156
-rw-r--r--com32/elflink/ldlinux/cli.c486
-rw-r--r--com32/elflink/ldlinux/colors.c184
-rw-r--r--com32/elflink/ldlinux/config.h52
-rw-r--r--com32/elflink/ldlinux/execute.c174
-rw-r--r--com32/elflink/ldlinux/get_key.c (renamed from com32/libutil/get_key.c)76
-rw-r--r--com32/elflink/ldlinux/getadv.c (renamed from com32/lib/syslinux/getadv.c)2
-rw-r--r--com32/elflink/ldlinux/kernel.c131
-rw-r--r--com32/elflink/ldlinux/ldlinux.c349
-rw-r--r--com32/elflink/ldlinux/loadhigh.c (renamed from core/fs/loadhigh.c)2
-rw-r--r--com32/elflink/ldlinux/msg.c228
-rw-r--r--com32/elflink/ldlinux/readconfig.c1572
-rw-r--r--com32/elflink/ldlinux/refstr.c106
-rw-r--r--com32/elflink/ldlinux/setadv.c (renamed from com32/lib/syslinux/setadv.c)2
-rw-r--r--com32/elflink/test_com32.c208
-rw-r--r--com32/gfxboot/Makefile4
-rw-r--r--com32/gfxboot/gfxboot.c2
-rw-r--r--com32/gpllib/Makefile14
-rw-r--r--com32/gpllib/disk/geom.c11
-rw-r--r--com32/gpllib/disk/read.c25
-rw-r--r--com32/gpllib/disk/write.c26
-rw-r--r--com32/gpllib/memory.c15
-rw-r--r--com32/hdt/Makefile5
-rw-r--r--com32/hdt/hdt-cli.c2
-rw-r--r--com32/hdt/hdt-common.c20
-rw-r--r--com32/hdt/hdt-common.h3
-rw-r--r--com32/hdt/hdt.c2
-rw-r--r--com32/include/byteswap.h50
-rw-r--r--com32/include/cli.h20
-rw-r--r--com32/include/com32.h14
-rw-r--r--com32/include/dprintf.h17
-rw-r--r--com32/include/errno.h21
-rw-r--r--com32/include/fcntl.h1
-rw-r--r--com32/include/hw/vga.h104
-rw-r--r--com32/include/klibc/compiler.h15
-rw-r--r--com32/include/linux/list.h464
-rw-r--r--com32/include/menu.h (renamed from com32/menu/menu.h)10
-rw-r--r--com32/include/netinet/in.h50
-rw-r--r--com32/include/refstr.h (renamed from com32/menu/refstr.h)0
-rw-r--r--com32/include/sort.h18
-rw-r--r--com32/include/stddef.h8
-rw-r--r--com32/include/stdio.h28
-rw-r--r--com32/include/sys/cpu.h31
-rw-r--r--com32/include/sys/elfcommon.h231
-rw-r--r--com32/include/sys/exec.h79
-rw-r--r--com32/include/sys/module.h374
-rw-r--r--com32/include/sys/times.h8
-rw-r--r--com32/include/syslinux/boot.h13
-rw-r--r--com32/include/syslinux/config.h13
-rw-r--r--com32/include/syslinux/debug.h15
-rw-r--r--com32/include/syslinux/disk.h7
-rw-r--r--com32/include/syslinux/features.h50
-rw-r--r--com32/include/syslinux/pmapi.h5
-rw-r--r--com32/include/syslinux/pxe_api.h26
-rw-r--r--com32/include/syslinux/sysappend.h (renamed from com32/lib/syslinux/video/forcetext.c)39
-rw-r--r--com32/lib/Makefile291
-rw-r--r--com32/lib/asprintf.c5
-rw-r--r--com32/lib/bufprintf.c8
-rw-r--r--com32/lib/chdir.c15
-rw-r--r--com32/lib/dprintf.c10
-rw-r--r--com32/lib/elf32.ld171
-rw-r--r--com32/lib/errno.c7
-rw-r--r--com32/lib/exit.c24
-rw-r--r--com32/lib/free.c113
-rw-r--r--com32/lib/getcwd.c3
-rw-r--r--com32/lib/init.h15
-rw-r--r--com32/lib/libpng/libpng.txt2959
-rw-r--r--com32/lib/lmalloc.c13
-rw-r--r--com32/lib/makeerrlist.pl98
-rw-r--r--com32/lib/malloc.c156
-rw-r--r--com32/lib/onexit.c3
-rw-r--r--com32/lib/realloc.c98
-rw-r--r--com32/lib/strerror.c28
-rw-r--r--com32/lib/sys/ansicon_write.c4
-rw-r--r--com32/lib/sys/exit.S41
-rw-r--r--com32/lib/sys/file.h3
-rw-r--r--com32/lib/sys/fileclose.c3
-rw-r--r--com32/lib/sys/fileread.c5
-rw-r--r--com32/lib/sys/gpxe.c19
-rw-r--r--com32/lib/sys/module/common.c599
-rw-r--r--com32/lib/sys/module/common.h73
-rw-r--r--com32/lib/sys/module/elf_module.c631
-rw-r--r--com32/lib/sys/module/elfutils.c89
-rw-r--r--com32/lib/sys/module/elfutils.h64
-rw-r--r--com32/lib/sys/module/exec.c234
-rw-r--r--com32/lib/sys/open.c6
-rw-r--r--com32/lib/sys/rawcon_read.c26
-rw-r--r--com32/lib/sys/rawcon_write.c8
-rw-r--r--com32/lib/sys/readdir.c30
-rw-r--r--com32/lib/sys/screensize.c2
-rw-r--r--com32/lib/sys/serial_write.c8
-rw-r--r--com32/lib/sys/stdcon_write.c15
-rw-r--r--com32/lib/sys/vesa/background.c1
-rw-r--r--com32/lib/sys/xserial_write.c8
-rw-r--r--com32/lib/syslinux/cleanup.c12
-rw-r--r--com32/lib/syslinux/config.c41
-rw-r--r--com32/lib/syslinux/debug.c95
-rw-r--r--com32/lib/syslinux/disk.c204
-rw-r--r--com32/lib/syslinux/idle.c5
-rw-r--r--com32/lib/syslinux/initramfs_file.c15
-rw-r--r--com32/lib/syslinux/ipappend.c23
-rw-r--r--com32/lib/syslinux/keyboard.c14
-rw-r--r--com32/lib/syslinux/load_linux.c147
-rw-r--r--com32/lib/syslinux/localboot.c10
-rw-r--r--com32/lib/syslinux/pxe_dns.c14
-rw-r--r--com32/lib/syslinux/pxe_get_cached.c11
-rw-r--r--com32/lib/syslinux/pxe_get_nic.c12
-rw-r--r--com32/lib/syslinux/run_command.c9
-rw-r--r--com32/lib/syslinux/run_default.c10
-rw-r--r--com32/lib/syslinux/runimage.c37
-rw-r--r--com32/lib/syslinux/serial.c21
-rw-r--r--com32/lib/syslinux/shuffle.c13
-rw-r--r--com32/lib/syslinux/version.c21
-rw-r--r--com32/lib/syslinux/video/fontquery.c16
-rw-r--r--com32/lib/syslinux/video/reportmode.c11
-rw-r--r--com32/lib/vdprintf.c13
-rw-r--r--com32/lib/zalloc.c17
-rw-r--r--com32/libupload/upload_tftp.c18
-rw-r--r--com32/libutil/Makefile17
-rw-r--r--com32/libutil/ansiraw.c19
-rw-r--r--com32/libutil/include/getkey.h3
-rw-r--r--com32/libutil/quicksort.c59
-rw-r--r--com32/lua/etc/luavs.bat56
-rw-r--r--com32/lua/src/Makefile13
-rw-r--r--com32/lua/src/vesa.c31
-rw-r--r--com32/mboot/Makefile2
-rw-r--r--com32/mboot/initvesa.c43
-rw-r--r--com32/mboot/mem.c35
-rw-r--r--com32/menu/Makefile4
-rw-r--r--com32/menu/execute.c69
-rw-r--r--com32/menu/menumain.c12
-rw-r--r--com32/menu/readconfig.c43
-rw-r--r--com32/modules/Makefile16
-rw-r--r--com32/modules/cat.c2
-rw-r--r--com32/modules/cmd.c2
-rw-r--r--com32/modules/config.c2
-rw-r--r--com32/modules/cptime.c284
-rw-r--r--com32/modules/cpuid.c2
-rw-r--r--com32/modules/cpuidtest.c1
-rw-r--r--com32/modules/debug.c54
-rw-r--r--com32/modules/dir.c175
-rw-r--r--com32/modules/disk.c2
-rw-r--r--com32/modules/dmitest.c1
-rw-r--r--com32/modules/elf.c2
-rw-r--r--com32/modules/ethersel.c1
-rw-r--r--com32/modules/gpxecmd.c18
-rw-r--r--com32/modules/hexdump.c245
-rw-r--r--com32/modules/host.c44
-rw-r--r--com32/modules/ifplop.c2
-rw-r--r--com32/modules/kbdmap.c2
-rw-r--r--com32/modules/linux.c202
-rw-r--r--com32/modules/ls.c2
-rw-r--r--com32/modules/meminfo.c20
-rw-r--r--com32/modules/pcitest.c2
-rw-r--r--com32/modules/pmload.c2
-rw-r--r--com32/modules/poweroff.c88
-rw-r--r--com32/modules/prdhcp.c4
-rw-r--r--com32/modules/pwd.c1
-rw-r--r--com32/modules/pxechn.c24
-rw-r--r--com32/modules/sanboot.c18
-rw-r--r--com32/modules/sdi.c2
-rw-r--r--com32/modules/vesainfo.c46
-rw-r--r--com32/modules/vpdtest.c1
-rw-r--r--com32/modules/whichsys.c2
-rw-r--r--com32/rosh/Makefile2
-rw-r--r--com32/samples/Makefile4
-rw-r--r--com32/samples/hello.c37
-rw-r--r--com32/samples/resolv.c17
-rw-r--r--com32/sysdump/Makefile4
-rw-r--r--com32/sysdump/README2
-rw-r--r--com32/sysdump/main.c1
-rw-r--r--core/Makefile103
-rw-r--r--core/abort.inc84
-rw-r--r--core/bios.inc1
-rw-r--r--core/bootsect.inc253
-rw-r--r--core/call16.c5
-rw-r--r--core/callback.inc19
-rw-r--r--core/cleanup.c46
-rw-r--r--core/cleanup.inc60
-rw-r--r--core/cmdline.inc101
-rw-r--r--core/com32.inc85
-rw-r--r--core/comboot.inc642
-rw-r--r--core/common.inc10
-rw-r--r--core/configinit.inc51
-rw-r--r--core/conio.c266
-rw-r--r--core/conio.inc431
-rw-r--r--core/console.c7
-rw-r--r--core/debug.c9
-rw-r--r--core/diskboot.inc6
-rw-r--r--core/diskfs.inc96
-rw-r--r--core/diskstart.inc7
-rw-r--r--core/dmi.c383
-rw-r--r--core/elflink/common.h62
-rw-r--r--core/elflink/elfutils.h67
-rw-r--r--core/elflink/load_env32.c249
-rw-r--r--core/errno.c4
-rw-r--r--core/extern.inc64
-rw-r--r--core/font.c181
-rw-r--r--core/font.inc152
-rw-r--r--core/fs/btrfs/btrfs.c3
-rw-r--r--core/fs/chdir.c9
-rw-r--r--core/fs/diskio.c9
-rw-r--r--core/fs/ext2/ext2.c3
-rw-r--r--core/fs/fat/fat.c32
-rw-r--r--core/fs/fs.c172
-rw-r--r--core/fs/getcwd.c2
-rw-r--r--core/fs/iso9660/iso9660.c7
-rw-r--r--core/fs/lib/chdir.c7
-rw-r--r--core/fs/lib/loadconfig.c4
-rw-r--r--core/fs/lib/searchconfig.c25
-rw-r--r--core/fs/newconfig.c41
-rw-r--r--core/fs/ntfs/ntfs.c2
-rw-r--r--core/fs/pxe/core.c262
-rw-r--r--core/fs/pxe/dhcp_option.c16
-rw-r--r--core/fs/pxe/dnsresolv.c321
-rw-r--r--core/fs/pxe/ftp.c280
-rw-r--r--core/fs/pxe/ftp_readdir.c141
-rw-r--r--core/fs/pxe/gpxeurl.c88
-rw-r--r--core/fs/pxe/http.c400
-rw-r--r--core/fs/pxe/http_readdir.c471
-rw-r--r--core/fs/pxe/idle.c83
-rw-r--r--core/fs/pxe/isr.c298
-rw-r--r--core/fs/pxe/pxe.c1151
-rw-r--r--core/fs/pxe/pxe.h173
-rw-r--r--core/fs/pxe/tcp.c78
-rw-r--r--core/fs/pxe/tftp.c417
-rw-r--r--core/fs/pxe/tftp.h54
-rw-r--r--core/fs/pxe/url.h33
-rw-r--r--core/fs/pxe/urlparse.c223
-rw-r--r--core/fs/readdir.c9
-rw-r--r--core/fs/xfs/misc.h50
-rw-r--r--core/fs/xfs/xfs.c431
-rw-r--r--core/fs/xfs/xfs.h757
-rw-r--r--core/fs/xfs/xfs_ag.h189
-rw-r--r--core/fs/xfs/xfs_dinode.c61
-rw-r--r--core/fs/xfs/xfs_dinode.h23
-rw-r--r--core/fs/xfs/xfs_dir2.c793
-rw-r--r--core/fs/xfs/xfs_dir2.h69
-rw-r--r--core/fs/xfs/xfs_fs.h501
-rw-r--r--core/fs/xfs/xfs_readdir.c388
-rw-r--r--core/fs/xfs/xfs_readdir.h30
-rw-r--r--core/fs/xfs/xfs_sb.h206
-rw-r--r--core/fs/xfs/xfs_types.h135
-rw-r--r--core/getc.inc415
-rw-r--r--core/graphics.c373
-rw-r--r--core/graphics.inc353
-rw-r--r--core/hello.c78
-rw-r--r--core/highmem.inc158
-rw-r--r--core/idle.c12
-rw-r--r--core/idle.inc81
-rw-r--r--core/include/bios.h114
-rw-r--r--core/include/core.h84
-rw-r--r--core/include/ctype.h13
-rw-r--r--core/include/fs.h49
-rw-r--r--core/include/graphics.h (renamed from com32/lib/syslinux/features.c)50
-rw-r--r--core/include/kaboom.h11
-rw-r--r--core/include/localboot.h (renamed from com32/lib/sys/times.c)19
-rw-r--r--core/include/mbox.h60
-rw-r--r--core/include/net.h36
-rw-r--r--core/include/thread.h116
-rw-r--r--core/include/timer.h21
-rw-r--r--core/init.c92
-rw-r--r--core/init.inc80
-rw-r--r--core/isolinux.asm142
-rw-r--r--core/kaboom.c14
-rw-r--r--core/kernel.inc3
-rw-r--r--core/keywords2
-rw-r--r--core/keywords.inc6
-rw-r--r--core/layout.inc23
-rw-r--r--core/ldlinux.asm2
-rw-r--r--core/legacynet/core.c228
-rw-r--r--core/legacynet/dnsresolv.c388
-rw-r--r--core/legacynet/idle.c112
-rw-r--r--core/legacynet/portnum.c (renamed from core/fs/pxe/portnum.c)2
-rw-r--r--core/loadhigh.inc60
-rw-r--r--core/localboot.c91
-rw-r--r--core/localboot.inc76
-rw-r--r--core/lwip/CHANGELOG3050
-rw-r--r--core/lwip/COPYING33
-rw-r--r--core/lwip/FILES4
-rw-r--r--core/lwip/README89
-rw-r--r--core/lwip/UPGRADING144
-rw-r--r--core/lwip/doc/FILES6
-rw-r--r--core/lwip/doc/contrib.txt63
-rw-r--r--core/lwip/doc/rawapi.txt505
-rw-r--r--core/lwip/doc/savannah.txt135
-rw-r--r--core/lwip/doc/snmp_agent.txt181
-rw-r--r--core/lwip/doc/sys_arch.txt216
-rw-r--r--core/lwip/src/FILES13
-rw-r--r--core/lwip/src/api/api_lib.c740
-rw-r--r--core/lwip/src/api/api_msg.c1535
-rw-r--r--core/lwip/src/api/err.c75
-rw-r--r--core/lwip/src/api/netbuf.c245
-rw-r--r--core/lwip/src/api/netdb.c352
-rw-r--r--core/lwip/src/api/netifapi.c160
-rw-r--r--core/lwip/src/api/sockets.c2347
-rw-r--r--core/lwip/src/api/tcpip.c460
-rw-r--r--core/lwip/src/arch/sys_arch.c131
-rw-r--r--core/lwip/src/core/def.c108
-rw-r--r--core/lwip/src/core/dhcp.c1745
-rw-r--r--core/lwip/src/core/dns.c975
-rw-r--r--core/lwip/src/core/init.c306
-rw-r--r--core/lwip/src/core/ipv4/autoip.c536
-rw-r--r--core/lwip/src/core/ipv4/icmp.c335
-rw-r--r--core/lwip/src/core/ipv4/igmp.c817
-rw-r--r--core/lwip/src/core/ipv4/inet.c42
-rw-r--r--core/lwip/src/core/ipv4/inet_chksum.c450
-rw-r--r--core/lwip/src/core/ipv4/ip.c857
-rw-r--r--core/lwip/src/core/ipv4/ip_addr.c312
-rw-r--r--core/lwip/src/core/ipv4/ip_frag.c863
-rw-r--r--core/lwip/src/core/mem.c642
-rw-r--r--core/lwip/src/core/memp.c469
-rw-r--r--core/lwip/src/core/netif.c752
-rw-r--r--core/lwip/src/core/pbuf.c1156
-rw-r--r--core/lwip/src/core/raw.c354
-rw-r--r--core/lwip/src/core/snmp/asn1_dec.c657
-rw-r--r--core/lwip/src/core/snmp/asn1_enc.c611
-rw-r--r--core/lwip/src/core/snmp/mib2.c4146
-rw-r--r--core/lwip/src/core/snmp/mib_structs.c1174
-rw-r--r--core/lwip/src/core/snmp/msg_in.c1437
-rw-r--r--core/lwip/src/core/snmp/msg_out.c681
-rw-r--r--core/lwip/src/core/stats.c176
-rw-r--r--core/lwip/src/core/sys.c66
-rw-r--r--core/lwip/src/core/tcp.c1635
-rw-r--r--core/lwip/src/core/tcp_in.c1567
-rw-r--r--core/lwip/src/core/tcp_out.c1468
-rw-r--r--core/lwip/src/core/timers.c483
-rw-r--r--core/lwip/src/core/udp.c966
-rw-r--r--core/lwip/src/include/arch/cc.h50
-rw-r--r--core/lwip/src/include/arch/perf.h7
-rw-r--r--core/lwip/src/include/arch/sys_arch.h85
-rw-r--r--core/lwip/src/include/ipv4/lwip/autoip.h119
-rw-r--r--core/lwip/src/include/ipv4/lwip/icmp.h111
-rw-r--r--core/lwip/src/include/ipv4/lwip/igmp.h106
-rw-r--r--core/lwip/src/include/ipv4/lwip/inet.h105
-rw-r--r--core/lwip/src/include/ipv4/lwip/inet_chksum.h90
-rw-r--r--core/lwip/src/include/ipv4/lwip/ip.h213
-rw-r--r--core/lwip/src/include/ipv4/lwip/ip_addr.h244
-rw-r--r--core/lwip/src/include/ipv4/lwip/ip_frag.h88
-rw-r--r--core/lwip/src/include/lwip/api.h284
-rw-r--r--core/lwip/src/include/lwip/api_msg.h174
-rw-r--r--core/lwip/src/include/lwip/arch.h238
-rw-r--r--core/lwip/src/include/lwip/debug.h98
-rw-r--r--core/lwip/src/include/lwip/def.h127
-rw-r--r--core/lwip/src/include/lwip/dhcp.h242
-rw-r--r--core/lwip/src/include/lwip/dns.h124
-rw-r--r--core/lwip/src/include/lwip/err.h85
-rw-r--r--core/lwip/src/include/lwip/init.h72
-rw-r--r--core/lwip/src/include/lwip/mem.h122
-rw-r--r--core/lwip/src/include/lwip/memp.h116
-rw-r--r--core/lwip/src/include/lwip/memp_std.h122
-rw-r--r--core/lwip/src/include/lwip/netbuf.h101
-rw-r--r--core/lwip/src/include/lwip/netdb.h124
-rw-r--r--core/lwip/src/include/lwip/netif.h315
-rw-r--r--core/lwip/src/include/lwip/netifapi.h108
-rw-r--r--core/lwip/src/include/lwip/opt.h2071
-rw-r--r--core/lwip/src/include/lwip/pbuf.h154
-rw-r--r--core/lwip/src/include/lwip/raw.h98
-rw-r--r--core/lwip/src/include/lwip/sio.h141
-rw-r--r--core/lwip/src/include/lwip/snmp.h367
-rw-r--r--core/lwip/src/include/lwip/snmp_asn1.h101
-rw-r--r--core/lwip/src/include/lwip/snmp_msg.h315
-rw-r--r--core/lwip/src/include/lwip/snmp_structs.h268
-rw-r--r--core/lwip/src/include/lwip/sockets.h376
-rw-r--r--core/lwip/src/include/lwip/stats.h292
-rw-r--r--core/lwip/src/include/lwip/sys.h331
-rw-r--r--core/lwip/src/include/lwip/tcp.h377
-rw-r--r--core/lwip/src/include/lwip/tcp_impl.h471
-rw-r--r--core/lwip/src/include/lwip/tcpip.h159
-rw-r--r--core/lwip/src/include/lwip/timers.h98
-rw-r--r--core/lwip/src/include/lwip/udp.h171
-rw-r--r--core/lwip/src/include/lwipopts.h73
-rw-r--r--core/lwip/src/include/netif/etharp.h221
-rw-r--r--core/lwip/src/include/netif/ppp_oe.h190
-rw-r--r--core/lwip/src/include/netif/slipif.h51
-rw-r--r--core/lwip/src/netif/FILES29
-rw-r--r--core/lwip/src/netif/etharp.c1318
-rw-r--r--core/lwip/src/netif/ethernetif.c318
-rw-r--r--core/lwip/src/netif/ppp/auth.c1334
-rw-r--r--core/lwip/src/netif/ppp/auth.h111
-rw-r--r--core/lwip/src/netif/ppp/chap.c908
-rw-r--r--core/lwip/src/netif/ppp/chap.h150
-rw-r--r--core/lwip/src/netif/ppp/chpms.c396
-rw-r--r--core/lwip/src/netif/ppp/chpms.h64
-rw-r--r--core/lwip/src/netif/ppp/fsm.c890
-rw-r--r--core/lwip/src/netif/ppp/fsm.h157
-rw-r--r--core/lwip/src/netif/ppp/ipcp.c1411
-rw-r--r--core/lwip/src/netif/ppp/ipcp.h106
-rw-r--r--core/lwip/src/netif/ppp/lcp.c2066
-rw-r--r--core/lwip/src/netif/ppp/lcp.h151
-rw-r--r--core/lwip/src/netif/ppp/magic.c80
-rw-r--r--core/lwip/src/netif/ppp/magic.h63
-rw-r--r--core/lwip/src/netif/ppp/md5.c320
-rw-r--r--core/lwip/src/netif/ppp/md5.h55
-rw-r--r--core/lwip/src/netif/ppp/pap.c628
-rw-r--r--core/lwip/src/netif/ppp/pap.h118
-rw-r--r--core/lwip/src/netif/ppp/ppp.c2020
-rw-r--r--core/lwip/src/netif/ppp/ppp.h483
-rw-r--r--core/lwip/src/netif/ppp/ppp_oe.c1132
-rw-r--r--core/lwip/src/netif/ppp/pppdebug.h73
-rw-r--r--core/lwip/src/netif/ppp/randm.c249
-rw-r--r--core/lwip/src/netif/ppp/randm.h81
-rw-r--r--core/lwip/src/netif/ppp/vj.c652
-rw-r--r--core/lwip/src/netif/ppp/vj.h156
-rw-r--r--core/lwip/src/netif/slipif.c367
-rw-r--r--core/lwip/src/netif/undiif.c1607
-rw-r--r--core/lzo/enter.ash3
-rw-r--r--core/lzo/leave.ash3
-rw-r--r--core/lzo/lzo1c_d.ash3
-rw-r--r--core/lzo/lzo1f_d.ash3
-rw-r--r--core/lzo/lzo1x_d.ash3
-rw-r--r--core/lzo/lzo1x_f2.S (renamed from core/lzo/lzo1x_f1.S)13
-rw-r--r--core/lzo/lzo_asm.h23
-rw-r--r--core/macros.inc5
-rw-r--r--core/mem/free.c28
-rw-r--r--core/mem/init.c106
-rw-r--r--core/mem/malloc.c151
-rw-r--r--core/mem/malloc.h58
-rw-r--r--core/parsecmd.inc129
-rw-r--r--core/parseconfig.inc473
-rw-r--r--core/path.c42
-rw-r--r--core/plaincon.c34
-rw-r--r--core/plaincon.inc24
-rw-r--r--core/pm.inc23
-rw-r--r--core/pmapi.c5
-rw-r--r--core/printf.c20
-rw-r--r--core/pxe.inc8
-rw-r--r--core/pxeboot.c40
-rw-r--r--core/pxeisr.inc172
-rw-r--r--core/pxelinux.asm267
-rw-r--r--core/rawcon.c93
-rw-r--r--core/rawcon.inc75
-rw-r--r--core/runkernel.inc684
-rw-r--r--core/serirq.c204
-rw-r--r--core/serirq.inc219
-rw-r--r--core/stack.inc2
-rw-r--r--core/strncasecmp.c24
-rw-r--r--core/sysappend.c120
-rw-r--r--core/syslinux.ld84
-rw-r--r--core/thread/exit_thread.c30
-rw-r--r--core/thread/idle_thread.c27
-rw-r--r--core/thread/kill_thread.c42
-rw-r--r--core/thread/mbox.c63
-rw-r--r--core/thread/root_thread.c11
-rw-r--r--core/thread/schedule.c91
-rw-r--r--core/thread/sem_asm.S16
-rw-r--r--core/thread/semaphore.c87
-rw-r--r--core/thread/start_thread.c69
-rw-r--r--core/thread/thread_asm.S37
-rw-r--r--core/thread/timeout.c41
-rw-r--r--core/timer.inc5
-rw-r--r--core/ui.inc743
-rw-r--r--core/writehex.c70
-rw-r--r--core/writestr.c47
-rw-r--r--core/writestr.inc47
-rw-r--r--diag/geodsp/Makefile2
-rw-r--r--diag/mbr/README2
-rw-r--r--doc/chain.txt41
-rw-r--r--doc/comboot.txt940
-rw-r--r--doc/cptime.txt50
-rw-r--r--doc/extlinux.txt4
-rw-r--r--doc/mboot.txt2
-rw-r--r--doc/pxelinux.txt41
-rw-r--r--doc/syslinux.txt180
-rw-r--r--dos/argv.c62
-rw-r--r--dos/crt0.S47
-rw-r--r--dos/dosexe.ld27
-rw-r--r--dos/free.c4
-rw-r--r--dos/getsetsl.c26
-rw-r--r--dos/ldlinux.S14
-rw-r--r--dos/mystuff.h59
-rw-r--r--dos/syslinux.c116
-rw-r--r--extlinux/Makefile1
-rw-r--r--extlinux/main.c313
-rw-r--r--extlinux/misc.h50
-rw-r--r--extlinux/xfs.h25
-rw-r--r--extlinux/xfs_fs.h501
-rw-r--r--extlinux/xfs_sb.h476
-rw-r--r--extlinux/xfs_types.h135
-rw-r--r--libinstaller/Makefile5
-rw-r--r--libinstaller/syslinux.h3
-rw-r--r--libinstaller/syslxfs.h5
-rw-r--r--libinstaller/syslxint.h17
-rw-r--r--libinstaller/syslxmod.c2
-rw-r--r--linux/Makefile1
-rwxr-xr-xlinux/syslinux.c55
-rw-r--r--lzo/prepcore.c2
-rw-r--r--man/syslinux.138
-rw-r--r--mbr/gptmbr.S73
-rw-r--r--mbr/mbr.S13
-rw-r--r--mk/com32.mk12
-rw-r--r--mk/devel.mk9
-rw-r--r--mk/elf.mk89
-rw-r--r--mk/embedded.mk1
-rw-r--r--mk/lib.mk8
-rw-r--r--mk/rosh.mk2
-rw-r--r--mk/syslinux.mk13
-rw-r--r--modules/Makefile61
-rw-r--r--modules/int18.asm16
-rw-r--r--modules/poweroff.asm102
-rw-r--r--modules/pxechain.asm558
-rw-r--r--modules/ver.asm606
-rwxr-xr-xmtools/Makefile1
-rwxr-xr-xmtools/syslinux.c129
-rw-r--r--txt/.gitignore7
-rw-r--r--txt/Makefile113
-rw-r--r--txt/com-bug.txt11
-rw-r--r--txt/com-derv.txt11
-rw-r--r--txt/com-name.txt12
-rw-r--r--txt/com-rpt.txt22
-rw-r--r--txt/hello.txt16
-rw-r--r--txt/isolinux.txt116
-rw-r--r--txt/pxelinux.txt461
-rw-r--r--txt/syslinux-cli.txt93
-rw-r--r--txt/syslinux.cfg.txt697
-rw-r--r--txt/syslinux.txt219
-rw-r--r--version2
-rw-r--r--win/syslinux.c127
-rw-r--r--win32/Makefile1
-rw-r--r--win64/Makefile1
550 files changed, 93755 insertions, 12673 deletions
diff --git a/.gitignore b/.gitignore
index 24435388..867e8220 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@
\#*
.\#*
.depend
+/com32/lib/errlist.c
/com32/lib/sys/vesa/alphatbl.c
/diag/geodsp/mk-lba-img
/extlinux/extlinux
@@ -47,3 +48,6 @@
/utils/mkdiskimage
/version.h
/version.mk
+*GPATH
+*GRTAGS
+*GTAGS
diff --git a/Makefile b/Makefile
index 1b419aea..be3425b8 100644
--- a/Makefile
+++ b/Makefile
@@ -30,18 +30,23 @@ include $(MAKEDIR)/syslinux.mk
# directories.
#
-# List of module objects that should be installed for all derivatives
-MODULES = memdisk/memdisk memdump/memdump.com modules/*.com \
+MODULES = memdisk/memdisk memdump/memdump.com \
com32/menu/*.c32 com32/modules/*.c32 com32/mboot/*.c32 \
com32/hdt/*.c32 com32/rosh/*.c32 com32/gfxboot/*.c32 \
- com32/sysdump/*.c32 com32/lua/src/*.c32 com32/chain/*.c32
+ com32/sysdump/*.c32 com32/lua/src/*.c32 com32/chain/*.c32 \
+ com32/lib/*.c32 com32/libutil/*.c32 com32/gpllib/*.c32 \
+ com32/elflink/ldlinux/*.c32 com32/cmenu/libmenu/*.c32
+
+# List of module objects that should be installed for all derivatives
+INSTALLABLE_MODULES = $(MODULES)
# syslinux.exe is BTARGET so as to not require everyone to have the
# mingw suite installed
BTARGET = version.gen version.h version.mk
BOBJECTS = $(BTARGET) \
mbr/*.bin \
- core/pxelinux.0 core/isolinux.bin core/isolinux-debug.bin \
+ core/pxelinux.0 core/lpxelinux.0 \
+ core/isolinux.bin core/isolinux-debug.bin \
gpxe/gpxelinux.0 dos/syslinux.com \
win32/syslinux.exe win64/syslinux64.exe \
dosutil/*.com dosutil/*.sys \
@@ -53,8 +58,8 @@ BOBJECTS = $(BTARGET) \
# Note: libinstaller is both a BSUBDIR and an ISUBDIR. It contains
# files that depend only on the B phase, but may have to be regenerated
# for "make installer".
-BSUBDIRS = codepage com32 lzo core memdisk modules mbr memdump gpxe sample \
- diag libinstaller dos win32 win64 dosutil
+BSUBDIRS = codepage com32 lzo core memdisk mbr memdump gpxe sample \
+ diag libinstaller dos win32 win64 dosutil txt
ITARGET =
IOBJECTS = $(ITARGET) \
utils/gethostip utils/isohybrid utils/mkdiskimage \
@@ -68,8 +73,8 @@ INSTALL_SBIN = extlinux/extlinux
# Things to install in /usr/lib/syslinux
INSTALL_AUX = core/pxelinux.0 gpxe/gpxelinux.0 gpxe/gpxelinuxk.0 \
core/isolinux.bin core/isolinux-debug.bin \
- dos/syslinux.com \
- mbr/*.bin $(MODULES)
+ dos/syslinux.com core/lpxelinux.0 \
+ mbr/*.bin $(INSTALLABLE_MODULES)
INSTALL_AUX_OPT = win32/syslinux.exe win64/syslinux64.exe
INSTALL_DIAG = diag/mbr/handoff.bin \
diag/geodsp/geodsp1s.img.xz diag/geodsp/geodspms.img.xz
@@ -78,11 +83,11 @@ INSTALL_DIAG = diag/mbr/handoff.bin \
INSTALLSUBDIRS = com32 utils dosutil
# Things to install in /boot/extlinux
-EXTBOOTINSTALL = $(MODULES)
+EXTBOOTINSTALL = $(INSTALLABLE_MODULES)
# Things to install in /tftpboot
-NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 \
- $(MODULES)
+NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 core/lpxelinux.0 \
+ $(INSTALLABLE_MODULES)
all:
$(MAKE) all-local
diff --git a/NEWS b/NEWS
index 7f696fd6..b513c88b 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,78 @@ Starting with 1.47, changes marked with SYSLINUX, PXELINUX, ISOLINUX
or EXTLINUX apply to that specific program only; other changes apply
to all derivatives.
+Changes in 5.11:
+ * Dynamic debug support: Add new module, debug.c32, that allows
+ debug code to be dynamically enabled and disabled at runtime.
+
+Changes in 5.10:
+ * PXELINUX: An entirely new network implementation based on
+ the lwIP embedded TCP/IP stack. As a result, plain PXELINUX
+ can now support HTTP and FTP without gPXE/iPXE. ls/readdir
+ functionality is supported over HTTP with an indexing
+ webserver, or over FTP with most common FTP servers. For the
+ new network stack use lpxelinux.0. For the legacy stack use
+ pxelinux.0.
+ * Rename the "ipappend" option to "sysappend" ("ipappend" is
+ still accepted as an alias) and make it available for all
+ derivatives. Add additional strings derived from the system
+ DMI/SMBIOS information if available.
+ * "sysappend" strings are also sent as http cookies, with the
+ prefix _Syslinux_ added, on all http transfers. This can be
+ overridden with the SENDCOOKIES configuration file command.
+ * poweroff.c32: A new module to power off a system via APM. It
+ replaces the poweroff COMBOOT module (Sebastian Herbszt).
+ * PXELINUX: Fix booting with DHCP options 209 and 210 which was
+ broken in 5.00.
+ * Handle loading kernel images with no protected mode code. A
+ legitimate kernel image can consist solely of real-mode code.
+ The support for booting such images was broken in 5.00 (Josh Triplett).
+ * Fix a regression in the .psf font file loader introduced
+ in 5.00.
+
+Changes in 5.01:
+ * txt/: A new AsciiDoc documentation set (work-in-progress)
+ (Gene Cumm).
+ * core: Fix a bug in the realloc() implementation that caused
+ machines to appear to run out of free memory.
+ * ldlinux: Fix multiple buffer overflows in cmdline parsing
+ code that resulted in files failing to run and cmdlines
+ being truncated.
+ * core: Fix debug build by tagging __bad_SEG() with __export.
+ * com32: Restrict library filenames to 8.3 format.
+ * EXTLINUX: Fix installation and subdirectory patching.
+ * ISOLINUX: Fix booting isohybrid images that are over 32K.
+ * com32: Strip modules to reduce their size.
+ * XFS: Implement directory block cache and fix
+ shortform-directory lookup (Paulo Alcantara).
+
+Changes in 5.00:
+ * com32: Switched from the COM32 object format to ELF as it is
+ a much more powerful format that allows undefined symbols to
+ be resolved at runtime and dynamic loading of module
+ dependencies, which means modules now become shared object
+ files instead of statically linked binaries - reducing both
+ disk space and runtime memory consumption.
+ * core: Split non-core functionality into ldlinux.c32, which
+ is an ELF module loaded by the core that contains everything
+ the core doesn't require to boot the system, e.g. config
+ parser, command-line interface, etc.
+ * Replaced __intcall() calls with direct function calls now
+ that we can resolve undefined symbols at runtime, thanks to
+ the ELF object support. Now that we no longer need to go
+ through the 16-bit interrupt mechanism we can make full use
+ of the 32-bit execution environment. This change required
+ reimplementing lots of the 16-bit assembly code from core/
+ in C.
+ * com32: __com32.cs_bounce is gone now we always run in a
+ 32-bit environment once we execute ldlinux.c32.
+ * ldlinux: A new "PATH" directive was added to the ldlinux.c32
+ config parser that specifies a colon-separated list of
+ directories to search when attempting to load modules.
+ * ALL: Delete all references to/code for 16-bit COMBOOT files.
+ COMBOOT files (.cbt and .com) are no longer supported under
+ Syslinux.
+
Changes in 4.07:
* EXTLINUX: fix crash caused by dereferencing garbage pointer.
* Plug memory leak in searchdir which eventually leads to an
diff --git a/README b/README
index bb1aeb65..f00fd0f7 100644
--- a/README
+++ b/README
@@ -7,7 +7,6 @@ See the files in the doc directory for documentation about SYSLINUX:
extlinux.txt - Documentation specific to EXTLINUX.
menu.txt - About the menu systems.
usbkey.txt - About using SYSLINUX on USB keys.
- comboot.txt - About the extension API.
memdisk.txt - Documentation about MEMDISK.
Also see the files:
diff --git a/com32/Makefile b/com32/Makefile
index b59fd3f9..c4699cfd 100644
--- a/com32/Makefile
+++ b/com32/Makefile
@@ -1,5 +1,5 @@
-SUBDIRS = libupload tools lib gpllib libutil modules mboot menu samples rosh cmenu \
- hdt gfxboot sysdump lua/src chain
+SUBDIRS = libupload tools lib elflink/ldlinux gpllib libutil modules mboot \
+ menu samples elflink rosh cmenu hdt gfxboot sysdump lua/src chain
all tidy dist clean spotless install:
set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
diff --git a/com32/chain/Makefile b/com32/chain/Makefile
index 9d398a85..c7587eae 100644
--- a/com32/chain/Makefile
+++ b/com32/chain/Makefile
@@ -1,7 +1,9 @@
## -----------------------------------------------------------------------
##
-## Copyright 2001-2010 H. Peter Anvin - All Rights Reserved
-## Copyright 2010 Michal Soltys
+## Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
+## Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+## Copyright 2010 Shao Miller
+## Copyright 2010-2012 Michal Soltys
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -14,13 +16,14 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
OBJS = chain.o partiter.o utility.o options.o mangle.o
+CFLAGS += -fno-strict-aliasing
all: chain.c32
-chain.elf: $(OBJS) $(LIBS) $(C_LIBS)
+chain.elf: $(OBJS) $(C_LIBS)
$(LD) $(LDFLAGS) -o $@ $^
%.o: %.c
diff --git a/com32/chain/chain.c b/com32/chain/chain.c
index 30153c4d..ae95d45c 100644
--- a/com32/chain/chain.c
+++ b/com32/chain/chain.c
@@ -3,7 +3,7 @@
* Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
* Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
* Copyright 2010 Shao Miller
- * Copyright 2010 Michal Soltys
+ * Copyright 2010-2012 Michal Soltys
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,7 +34,6 @@
#include <syslinux/config.h>
#include <syslinux/disk.h>
#include <syslinux/video.h>
-#include "common.h"
#include "chain.h"
#include "utility.h"
#include "options.h"
@@ -72,14 +71,14 @@ static int find_by_sig(uint32_t mbr_sig,
for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
if (disk_get_params(drive, &diskinfo))
continue; /* Drive doesn't exist */
- if (!(boot_part = pi_begin(&diskinfo, 0)))
+ if (!(boot_part = pi_begin(&diskinfo, opt.piflags)))
continue;
/* Check for a MBR disk */
if (boot_part->type != typedos) {
pi_del(&boot_part);
continue;
}
- if (boot_part->sub.dos.disk_sig == mbr_sig) {
+ if (boot_part->dos.disk_sig == mbr_sig) {
goto ok;
}
}
@@ -103,22 +102,18 @@ static int find_by_guid(const struct guid *gpt_guid,
for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
if (disk_get_params(drive, &diskinfo))
continue; /* Drive doesn't exist */
- if (!(boot_part = pi_begin(&diskinfo, 0)))
+ if (!(boot_part = pi_begin(&diskinfo, opt.piflags)))
continue;
/* Check for a GPT disk */
if (boot_part->type != typegpt) {
pi_del(&boot_part);
continue;
}
- /* Check for a matching GPT disk guid */
- if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
- goto ok;
- }
- /* disk guid doesn't match, maybe partition guid will */
- while (!pi_next(&boot_part)) {
- if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
+ /* Check for a matching GPT disk/partition guid */
+ do {
+ if (!memcmp(&boot_part->gpt.part_guid, gpt_guid, sizeof *gpt_guid))
goto ok;
- }
+ } while (!pi_next(boot_part));
}
drive = -1;
ok:
@@ -139,7 +134,7 @@ static int find_by_label(const char *label, struct part_iter **_boot_part)
for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
if (disk_get_params(drive, &diskinfo))
continue; /* Drive doesn't exist */
- if (!(boot_part = pi_begin(&diskinfo, 0)))
+ if (!(boot_part = pi_begin(&diskinfo, opt.piflags)))
continue;
/* Check for a GPT disk */
if (!(boot_part->type == typegpt)) {
@@ -147,8 +142,8 @@ static int find_by_label(const char *label, struct part_iter **_boot_part)
continue;
}
/* Check for a matching partition */
- while (!pi_next(&boot_part)) {
- if (!strcmp(label, boot_part->sub.gpt.part_label))
+ while (!pi_next(boot_part)) {
+ if (!strcmp(label, boot_part->gpt.part_label))
goto ok;
}
}
@@ -160,8 +155,6 @@ ok:
static void do_boot(struct data_area *data, int ndata)
{
- uint16_t *const bios_fbm = (uint16_t *) 0x413;
- addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */
struct syslinux_memmap *mmap;
struct syslinux_movelist *mlist = NULL;
addr_t endimage;
@@ -172,7 +165,7 @@ static void do_boot(struct data_area *data, int ndata)
mmap = syslinux_memory_map();
if (!mmap) {
- error("Cannot read system memory map\n");
+ error("Cannot read system memory map.");
return;
}
@@ -181,7 +174,7 @@ static void do_boot(struct data_area *data, int ndata)
if (data[i].base + data[i].size > endimage)
endimage = data[i].base + data[i].size;
}
- if (endimage > dosmem)
+ if (endimage > dosmax)
goto too_big;
for (i = 0; i < ndata; i++) {
@@ -227,7 +220,7 @@ static void do_boot(struct data_area *data, int ndata)
static uint8_t swapstub[1024];
uint8_t *p;
- /* Note: we can't rely on either INT 13h nor the dosmem
+ /* Note: we can't rely on either INT 13h nor the dosmax
vector to be correct at this stage, so we have to use an
installer stub to put things in the right place.
Round the installer location to a 1K boundary so the only
@@ -247,14 +240,14 @@ static void do_boot(struct data_area *data, int ndata)
/* Mapping table; start out with identity mapping everything */
for (i = 0; i < 256; i++)
- p[i] = (uint8_t)i;
+ p[i] = i;
/* And the actual swap */
p[driveno] = swapdrive;
p[swapdrive] = driveno;
/* Adjust registers */
- opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
+ opt.regs.ds = opt.regs.cs = endimage >> 4;
opt.regs.esi.l = opt.regs.es = 0;
opt.regs.ecx.l = sizeof swapstub >> 2;
opt.regs.ip = 0x10; /* Installer offset */
@@ -273,17 +266,17 @@ static void do_boot(struct data_area *data, int ndata)
/* Force text mode */
syslinux_force_text_mode();
- fputs("Booting...\n", stdout);
+ puts("Booting...");
syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
- error("Chainboot failed!\n");
+ error("Chainboot failed !");
return;
too_big:
- error("Loader file too large\n");
+ error("Loader file too large.");
return;
enomem:
- error("Out of memory\n");
+ error("Out of memory.");
return;
}
@@ -300,23 +293,23 @@ int find_dp(struct part_iter **_iter)
if (!strncmp(opt.drivename, "mbr", 3)) {
if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
- error("Unable to find requested MBR signature.\n");
+ error("Unable to find requested MBR signature.");
goto bail;
}
} else if (!strncmp(opt.drivename, "guid", 4)) {
if (str_to_guid(opt.drivename + 5, &gpt_guid))
goto bail;
if (find_by_guid(&gpt_guid, &iter) < 0) {
- error("Unable to find requested GPT disk or partition by guid.\n");
+ error("Unable to find requested GPT disk or partition by guid.");
goto bail;
}
} else if (!strncmp(opt.drivename, "label", 5)) {
if (!opt.drivename[6]) {
- error("No label specified.\n");
+ error("No label specified.");
goto bail;
}
if (find_by_label(opt.drivename + 6, &iter) < 0) {
- error("Unable to find requested GPT partition by label.\n");
+ error("Unable to find requested GPT partition by label.");
goto bail;
}
} else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
@@ -328,13 +321,13 @@ int find_dp(struct part_iter **_iter)
if (disk_get_params(drive, &diskinfo))
goto bail;
/* this will start iteration over FDD, possibly raw */
- if (!(iter = pi_begin(&diskinfo, 0)))
+ if (!(iter = pi_begin(&diskinfo, opt.piflags)))
goto bail;
} else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
if (!is_phys(sdi->c.filesystem)) {
error("When syslinux is not booted from physical disk (or its emulation),\n"
- "'boot' and 'fs' are meaningless.\n");
+ "'boot' and 'fs' are meaningless.");
goto bail;
}
/* offsets match, but in case it changes in the future */
@@ -348,23 +341,23 @@ int find_dp(struct part_iter **_iter)
if (disk_get_params(drive, &diskinfo))
goto bail;
/* this will start iteration over disk emulation, possibly raw */
- if (!(iter = pi_begin(&diskinfo, 0)))
+ if (!(iter = pi_begin(&diskinfo, opt.piflags)))
goto bail;
/* 'fs' => we should lookup the syslinux partition number and use it */
if (!strcmp(opt.drivename, "fs")) {
- while (!pi_next(&iter)) {
- if (iter->start_lba == fs_lba)
+ do {
+ if (iter->abs_lba == fs_lba)
break;
- }
+ } while (!pi_next(iter));
/* broken part structure or other problems */
if (iter->status) {
- error("Can't find myself on the drive I booted from.\n");
+ error("Can't find myself on the drive I booted from.");
goto bail;
}
}
} else {
- error("Unparsable drive specification.\n");
+ error("Unparsable drive specification.");
goto bail;
}
/* main options done - only thing left is explicit partition specification,
@@ -377,15 +370,15 @@ int find_dp(struct part_iter **_iter)
do {
if (iter->index == partition)
break;
- } while (!pi_next(&iter));
+ } while (!pi_next(iter));
if (iter->status) {
- error("Requested disk / partition combination not found.\n");
+ error("Requested disk / partition combination not found.");
goto bail;
}
}
if (!(iter->di.disk & 0x80) && iter->index) {
- error("WARNING: Partitions on floppy devices may not work.\n");
+ warn("Partitions on floppy devices may not work.");
}
*_iter = iter;
@@ -401,51 +394,53 @@ static int setup_handover(const struct part_iter *iter,
struct data_area *data)
{
struct disk_dos_part_entry *ha;
- uint32_t synth_size;
- uint32_t *plen;
+ uint32_t synth_size = sizeof *ha;
- if (!iter->index) { /* implies typeraw or non-iterated */
+ /*
+ * we have to cover both non-iterated but otherwise properly detected
+ * gpt/dos schemes as well as raw disks; checking index for 0 covers both
+ */
+ if (iter->index == 0) {
uint32_t len;
/* RAW handover protocol */
- synth_size = sizeof(struct disk_dos_part_entry);
ha = malloc(synth_size);
if (!ha) {
- error("Could not build RAW hand-over record!\n");
+ critm();
goto bail;
}
len = ~0u;
if (iter->length < len)
- len = (uint32_t)iter->length;
- lba2chs(&ha->start, &iter->di, 0, l2c_cadd);
- lba2chs(&ha->end, &iter->di, len - 1, l2c_cadd);
+ len = iter->length;
+ lba2chs(&ha->start, &iter->di, 0, L2C_CADD);
+ lba2chs(&ha->end, &iter->di, len - 1, L2C_CADD);
ha->active_flag = 0x80;
ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */
ha->start_lba = 0;
ha->length = len;
} else if (iter->type == typegpt) {
+ uint32_t *plen;
/* GPT handover protocol */
- synth_size = sizeof(struct disk_dos_part_entry) +
- sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
+ synth_size += sizeof *plen + iter->gpt.pe_size;
ha = malloc(synth_size);
if (!ha) {
- error("Could not build GPT hand-over record!\n");
+ critm();
goto bail;
}
- lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
- lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
+ lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
+ lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
ha->active_flag = 0x80;
ha->ostype = 0xED;
/* All bits set by default */
ha->start_lba = ~0u;
ha->length = ~0u;
/* If these fit the precision, pass them on */
- if (iter->start_lba < ha->start_lba)
- ha->start_lba = (uint32_t)iter->start_lba;
+ if (iter->abs_lba < ha->start_lba)
+ ha->start_lba = iter->abs_lba;
if (iter->length < ha->length)
- ha->length = (uint32_t)iter->length;
+ ha->length = iter->length;
/* Next comes the GPT partition record length */
- plen = (uint32_t *) (ha + 1);
- plen[0] = (uint32_t)iter->sub.gpt.pe_size;
+ plen = (uint32_t *)(ha + 1);
+ plen[0] = iter->gpt.pe_size;
/* Next comes the GPT partition record copy */
memcpy(plen + 1, iter->record, plen[0]);
#ifdef DEBUG
@@ -453,20 +448,20 @@ static int setup_handover(const struct part_iter *iter,
disk_dos_part_dump(ha);
disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
#endif
+ /* the only possible case left is dos scheme */
} else if (iter->type == typedos) {
/* MBR handover protocol */
- synth_size = sizeof(struct disk_dos_part_entry);
ha = malloc(synth_size);
if (!ha) {
- error("Could not build MBR hand-over record!\n");
+ critm();
goto bail;
}
memcpy(ha, iter->record, synth_size);
/* make sure these match bios imaginations and are ebr agnostic */
- lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
- lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
- ha->start_lba = (uint32_t)iter->start_lba;
- ha->length = (uint32_t)iter->length;
+ lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
+ lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
+ ha->start_lba = iter->abs_lba;
+ ha->length = iter->length;
#ifdef DEBUG
dprintf("MBR handover:\n");
@@ -495,9 +490,9 @@ int main(int argc, char *argv[])
console_ansi_raw();
- memset(&fdat, 0, sizeof(fdat));
- memset(&hdat, 0, sizeof(hdat));
- memset(&sdat, 0, sizeof(sdat));
+ memset(&fdat, 0, sizeof fdat);
+ memset(&hdat, 0, sizeof hdat);
+ memset(&sdat, 0, sizeof sdat);
opt_set_defs();
if (opt_parse_args(argc, argv))
@@ -530,11 +525,11 @@ int main(int argc, char *argv[])
fdat.base = (opt.fseg << 4) + opt.foff;
if (loadfile(opt.file, &fdat.data, &fdat.size)) {
- error("Couldn't read the boot file.\n");
+ error("Couldn't read the boot file.");
goto bail;
}
- if (fdat.base + fdat.size - 1 > ADDRMAX) {
- error("The boot file is too big to load at this address.\n");
+ if (fdat.base + fdat.size > dosmax) {
+ error("The boot file is too big to load at this address.");
goto bail;
}
}
@@ -544,23 +539,23 @@ int main(int argc, char *argv[])
sdat.base = (opt.sseg << 4) + opt.soff;
sdat.size = iter->di.bps;
- if (sdat.base + sdat.size - 1 > ADDRMAX) {
- error("The sector cannot be loaded at such high address.\n");
+ if (sdat.base + sdat.size > dosmax) {
+ error("The sector cannot be loaded at such high address.");
goto bail;
}
- if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
- error("Couldn't read the sector.\n");
+ if (!(sdat.data = disk_read_sectors(&iter->di, iter->abs_lba, 1))) {
+ error("Couldn't read the sector.");
goto bail;
}
if (opt.save) {
if (!(sbck = malloc(sdat.size))) {
- error("Couldn't allocate cmp-buf for option 'save'.\n");
+ critm();
goto bail;
}
memcpy(sbck, sdat.data, sdat.size);
}
if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
- error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n");
+ warn("The sector won't be mmapped, as it would conflict with the boot file.");
opt.maps = false;
}
}
@@ -572,8 +567,8 @@ int main(int argc, char *argv[])
/* Verify possible conflicts */
if ( ( opt.file && overlap(&fdat, &hdat)) ||
( opt.maps && overlap(&sdat, &hdat)) ) {
- error("WARNING: Handover area won't be prepared,\n"
- "as it would conflict with the boot file and/or the sector.\n");
+ warn("Handover area won't be prepared,\n"
+ "as it would conflict with the boot file and/or the sector.");
opt.hand = false;
}
}
@@ -617,22 +612,22 @@ int main(int argc, char *argv[])
*/
if (opt.file)
- memcpy(data + ndata++, &fdat, sizeof(fdat));
+ memcpy(data + ndata++, &fdat, sizeof fdat);
if (opt.maps)
- memcpy(data + ndata++, &sdat, sizeof(sdat));
+ memcpy(data + ndata++, &sdat, sizeof sdat);
if (opt.hand)
- memcpy(data + ndata++, &hdat, sizeof(hdat));
+ memcpy(data + ndata++, &hdat, sizeof hdat);
#ifdef DEBUG
- printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
+ dprintf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
"iter->di C, H, S: %u, %u, %u\n",
iter->di.disk, iter->di.bps,
iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
iter->di.cyl, iter->di.head, iter->di.spt);
- printf("iter idx: %d\n", iter->index);
- printf("iter lba: %"PRIu64"\n", iter->start_lba);
+ dprintf("iter idx: %d\n", iter->index);
+ dprintf("iter lba: %"PRIu64"\n", iter->abs_lba);
if (opt.hand)
- printf("hand lba: %u\n",
+ dprintf("hand lba: %u\n",
((struct disk_dos_part_entry *)hdat.data)->start_lba);
#endif
@@ -644,7 +639,7 @@ int main(int argc, char *argv[])
if (ndata && !opt.brkchain) /* boot only if we actually chainload */
do_boot(data, ndata);
else
- error("Service-only run completed, exiting.\n");
+ puts("Service-only run completed, exiting.");
bail:
pi_del(&iter);
/* Free allocated areas */
diff --git a/com32/chain/chain.h b/com32/chain/chain.h
index fc481bc6..fb5914b1 100644
--- a/com32/chain/chain.h
+++ b/com32/chain/chain.h
@@ -1,5 +1,20 @@
-#ifndef _COM32_CHAIN_CHAIN_H
-#define _COM32_CHAIN_CHAIN_H
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_CHAIN_H
+#define COM32_CHAIN_CHAIN_H
#include <syslinux/movebits.h>
diff --git a/com32/chain/common.h b/com32/chain/common.h
deleted file mode 100644
index b170a732..00000000
--- a/com32/chain/common.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _COM32_CHAIN_COMMON_H
-#define _COM32_CHAIN_COMMON_H
-
-#define ADDRMAX 0x9EFFFu
-#define ADDRMIN 0x500u
-
-#endif
-
-/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/mangle.c b/com32/chain/mangle.c
index 8358106e..ffdaab8d 100644
--- a/com32/chain/mangle.c
+++ b/com32/chain/mangle.c
@@ -1,3 +1,33 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
#include <com32.h>
#include <stdlib.h>
#include <stdio.h>
@@ -5,7 +35,6 @@
#include <stdint.h>
#include <dprintf.h>
#include <syslinux/config.h>
-#include "common.h"
#include "chain.h"
#include "options.h"
#include "utility.h"
@@ -32,7 +61,7 @@ int manglef_isolinux(struct data_area *data)
sdi = syslinux_derivative_info();
if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
- error ("The isolinux= option is only valid when run from ISOLINUX.\n");
+ error("The isolinux= option is only valid when run from ISOLINUX.");
goto bail;
}
@@ -58,7 +87,7 @@ int manglef_isolinux(struct data_area *data)
file_lba = get_file_lba(opt.file);
if (file_lba == 0) {
- error("Failed to find LBA offset of the boot file\n");
+ error("Failed to find LBA offset of the boot file.");
goto bail;
}
/* Set it */
@@ -132,8 +161,8 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
if (!(opt.file && opt.grub))
return 0;
- if (data->size < sizeof(struct grub_stage2_patch_area)) {
- error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
+ if (data->size < sizeof *stage2) {
+ error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.");
goto bail;
}
stage2 = data->data;
@@ -144,7 +173,7 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
*/
if (stage2->compat_version_major != 3
|| stage2->compat_version_minor != 2) {
- error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
+ error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.");
goto bail;
}
@@ -174,7 +203,7 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
* 0-3: primary partitions
* 4-*: logical partitions
*/
- stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
+ stage2->install_partition.part1 = iter->index - 1;
/*
* Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
@@ -182,8 +211,8 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
* the default config filename "/boot/grub/menu.lst".
*/
if (opt.grubcfg) {
- if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
- error ("The config filename length can't exceed 88 characters.\n");
+ if (strlen(opt.grubcfg) >= sizeof stage2->config_file) {
+ error("The config filename length can't exceed 88 characters.");
goto bail;
}
@@ -224,19 +253,19 @@ int manglef_drmk(struct data_area *data)
dprintf(" fs_lba offset is %d\n", fs_lba);
/* DRMK only uses a DWORD */
if (fs_lba > 0xffffffff) {
- error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
+ error("LBA very large; Only using lower 32 bits; DRMK will probably fail.");
}
opt.regs.ss = opt.regs.fs = opt.regs.gs = 0; /* Used before initialized */
if (!realloc(data->data, tsize)) {
- error("Failed to realloc for DRMK.\n");
+ error("Failed to realloc for DRMK.");
goto bail;
}
data->size = tsize;
/* ds:bp is assumed by DRMK to be the boot sector */
/* offset 28 is the FAT HiddenSectors value */
- opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
+ opt.regs.ds = (tsize >> 4) + (opt.fseg - 2);
/* "Patch" into tail of the new space */
- *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba;
+ *(uint32_t *)((char*)data->data + tsize - 4) = fs_lba;
return 0;
bail:
@@ -246,29 +275,32 @@ bail:
/* Adjust BPB common function */
static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag)
{
- unsigned int off;
int type = bpb_detect(data->data, tag);
+ int off = drvoff_detect(type);
+ /* BPB: hidden sectors 64bit - exFAT only for now */
+ if (type == bpbEXF)
+ *(uint64_t *) ((char *)data->data + 0x40) = iter->abs_lba;
/* BPB: hidden sectors 32bit*/
- if (type >= bpbV34) {
- if (iter->start_lba < ~0u)
- *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
+ else if (bpbV34 <= type && type <= bpbV70) {
+ if (iter->abs_lba < ~0u)
+ *(uint32_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
else
/* won't really help much, but ... */
*(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
- }
/* BPB: hidden sectors 16bit*/
- if (bpbV30 <= type && type <= bpbV32) {
- if (iter->start_lba < 0xFFFF)
- *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
+ } else if (bpbV30 <= type && type <= bpbV32) {
+ if (iter->abs_lba < 0xFFFF)
+ *(uint16_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
else
/* won't really help much, but ... */
*(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
}
+
/* BPB: legacy geometry */
- if (type >= bpbV30) {
+ if (bpbV30 <= type && type <= bpbV70) {
if (iter->di.cbios)
- *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt);
+ *(uint32_t *)((char *)data->data + 0x18) = (iter->di.head << 16) | iter->di.spt;
else {
if (iter->di.disk & 0x80)
*(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
@@ -277,9 +309,8 @@ static int mangle_bpb(const struct part_iter *iter, struct data_area *data, cons
}
}
/* BPB: drive */
- if (drvoff_detect(type, &off)) {
- *(uint8_t *)((char *)data->data + off) = (uint8_t)
- (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
+ if (off >= 0) {
+ *(uint8_t *)((char *)data->data + off) = (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
}
return 0;
@@ -314,7 +345,7 @@ int mangles_bpb(const struct part_iter *iter, struct data_area *data)
int manglesf_bss(struct data_area *sec, struct data_area *fil)
{
int type1, type2;
- unsigned int cnt = 0;
+ size_t cnt = 0;
if (!(opt.sect && opt.file && opt.bss))
return 0;
@@ -323,12 +354,12 @@ int manglesf_bss(struct data_area *sec, struct data_area *fil)
type2 = bpb_detect(sec->data, "bss/sect");
if (!type1 || !type2) {
- error("Couldn't determine the BPB type for option 'bss'.\n");
+ error("Couldn't determine the BPB type for option 'bss'.");
goto bail;
}
if (type1 != type2) {
error("Option 'bss' can't be used,\n"
- "when a sector and a file have incompatible BPBs.\n");
+ "when a sector and a file have incompatible BPBs.");
goto bail;
}
@@ -348,6 +379,8 @@ int manglesf_bss(struct data_area *sec, struct data_area *fil)
cnt = 0x3C;
} else if (type1 <= bpbV70) {
cnt = 0x42;
+ } else if (type1 <= bpbEXF) {
+ cnt = 0x60;
}
memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
@@ -365,8 +398,8 @@ int mangles_save(const struct part_iter *iter, const struct data_area *data, voi
return 0;
if (memcmp(org, data->data, data->size)) {
- if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) {
- error("Cannot write the updated sector.\n");
+ if (disk_write_sectors(&iter->di, iter->abs_lba, data->data, 1)) {
+ error("Cannot write the updated sector.");
goto bail;
}
/* function can be called again */
@@ -388,7 +421,7 @@ int mangles_cmldr(struct data_area *data)
if (!(opt.sect && opt.cmldr))
return 0;
- memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
+ memcpy((char *)data->data + 3, cmldr_signature, sizeof cmldr_signature);
return 0;
}
@@ -397,18 +430,18 @@ int mangler_init(const struct part_iter *iter)
{
/* Set initial registry values */
if (opt.file) {
- opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
- opt.regs.ip = (uint16_t)opt.fip;
+ opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.fseg;
+ opt.regs.ip = opt.fip;
} else {
- opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
- opt.regs.ip = (uint16_t)opt.sip;
+ opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.sseg;
+ opt.regs.ip = opt.sip;
}
if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
opt.regs.esp.l = 0x7C00;
/* DOS kernels want the drive number in BL instead of DL. Indulge them. */
- opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
+ opt.regs.ebx.b[0] = opt.regs.edx.b[0] = iter->di.disk;
return 0;
}
@@ -418,7 +451,7 @@ int mangler_handover(const struct part_iter *iter, const struct data_area *data)
{
if (opt.file && opt.maps && !opt.hptr) {
opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
- opt.regs.ds = (uint16_t)opt.sseg;
+ opt.regs.ds = opt.sseg;
opt.regs.eax.l = 0;
} else if (opt.hand) {
/* base is really 0x7be */
@@ -442,7 +475,7 @@ int mangler_handover(const struct part_iter *iter, const struct data_area *data)
int mangler_grldr(const struct part_iter *iter)
{
if (opt.grldr)
- opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
+ opt.regs.edx.b[1] = iter->index - 1;
return 0;
}
@@ -450,15 +483,15 @@ int mangler_grldr(const struct part_iter *iter)
/*
* try to copy values from temporary iterator, if positions match
*/
-static void push_embr(struct part_iter *diter, struct part_iter *siter)
+static void mbrcpy(struct part_iter *diter, struct part_iter *siter)
{
- if (diter->sub.dos.cebr_lba == siter->sub.dos.cebr_lba &&
+ if (diter->dos.cebr_lba == siter->dos.cebr_lba &&
diter->di.disk == siter->di.disk) {
memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr));
}
}
-static int mpe_sethide(struct part_iter *iter, struct part_iter *miter)
+static int fliphide(struct part_iter *iter, struct part_iter *miter)
{
struct disk_dos_part_entry *dp;
static const uint16_t mask =
@@ -471,8 +504,8 @@ static int mpe_sethide(struct part_iter *iter, struct part_iter *miter)
if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
/* It's a hideable partition type */
- if (miter->index == iter->index || opt.hide & 4)
- t &= (uint8_t)(~0x10u); /* unhide */
+ if (miter->index == iter->index || opt.hide & HIDE_REV)
+ t &= ~0x10u; /* unhide */
else
t |= 0x10u; /* hide */
}
@@ -494,69 +527,98 @@ int manglepe_hide(struct part_iter *miter)
{
int wb = 0, werr = 0;
struct part_iter *iter = NULL;
- struct disk_dos_part_entry *dp;
int ridx;
- if (!opt.hide)
+ if (!(opt.hide & HIDE_ON))
return 0;
if (miter->type != typedos) {
- error("Options '*hide*' is meaningful only for legacy partition scheme.\n");
+ error("Option '[un]hide[all]' works only for legacy (DOS) partition scheme.");
return -1;
}
- if (miter->index < 1)
- error("WARNING: It's impossible to unhide a disk.\n");
-
- if (miter->index > 4 && !(opt.hide & 2))
- error("WARNING: your partition is beyond mbr, so it can't be unhidden without '*hideall'.\n");
+ if (miter->index > 4 && !(opt.hide & HIDE_EXT))
+ warn("Specified partition is logical, so it can't be unhidden without 'unhideall'.");
- if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */
+ if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
return -1;
- while (!pi_next(&iter) && !werr) {
- ridx = iter->rawindex;
- if (!(opt.hide & 2) && ridx > 4)
+ while (!pi_next(iter) && !werr) {
+ ridx = iter->index0;
+ if (!(opt.hide & HIDE_EXT) && ridx > 3)
break; /* skip when we're constrained to pri only */
- dp = (struct disk_dos_part_entry *)iter->record;
- if (dp->ostype)
- wb |= mpe_sethide(iter, miter);
+ if (iter->index != -1)
+ wb |= fliphide(iter, miter);
- if (ridx >= 4 && wb && !werr) {
- push_embr(miter, iter);
- werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ /*
+ * we have to update mbr and each extended partition, but only if
+ * changes (wb) were detected and there was no prior write error (werr)
+ */
+ if (ridx >= 3 && wb && !werr) {
+ mbrcpy(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
wb = 0;
}
}
- if (iter->status > PI_DONE)
+ if (pi_errored(iter))
goto bail;
- /* last write */
+ /* last update */
if (wb && !werr) {
- push_embr(miter, iter);
- werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ mbrcpy(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
}
if (werr)
- error("WARNING: failed to write E/MBR during '*hide*'\n");
+ warn("Failed to write E/MBR during '[un]hide[all]'.");
bail:
pi_del(&iter);
return 0;
}
-static int mpe_setchs(const struct disk_info *di,
- struct disk_dos_part_entry *dp,
- uint32_t lba1)
+static int updchs(struct part_iter *iter, int ext)
{
- uint32_t ochs1, ochs2;
+ struct disk_dos_part_entry *dp;
+ uint32_t ochs1, ochs2, lba;
+ dp = (struct disk_dos_part_entry *)iter->record;
+ if (!ext) {
+ /* primary or logical */
+ lba = (uint32_t)iter->abs_lba;
+ } else {
+ /* extended */
+ dp += 1;
+ lba = iter->dos.nebr_lba;
+ }
ochs1 = *(uint32_t *)dp->start;
ochs2 = *(uint32_t *)dp->end;
- lba2chs(&dp->start, di, lba1, l2c_cadd);
- lba2chs(&dp->end, di, lba1 + dp->length - 1, l2c_cadd);
+ /*
+ * We have to be a bit more careful here in case of 0 start and/or length;
+ * start = 0 would be converted to the beginning of the disk (C/H/S =
+ * 0/0/1) or the [B]EBR, length = 0 would actually set the end CHS to be
+ * lower than the start CHS.
+ *
+ * Both are harmless in case of a hole (and in non-hole case will make
+ * partiter complain about corrupt layout unless PIF_RELAX is set), but it
+ * makes everything look silly and not really correct.
+ *
+ * Thus the approach as seen below.
+ */
+
+ if (dp->start_lba || iter->index != -1) {
+ lba2chs(&dp->start, &iter->di, lba, L2C_CADD);
+ } else {
+ memset(&dp->start, 0, sizeof dp->start);
+ }
+
+ if ((dp->start_lba || iter->index != -1) && dp->length) {
+ lba2chs(&dp->end, &iter->di, lba + dp->length - 1, L2C_CADD);
+ } else {
+ memset(&dp->end, 0, sizeof dp->end);
+ }
return
*(uint32_t *)dp->start != ochs1 ||
@@ -570,45 +632,47 @@ int manglepe_fixchs(struct part_iter *miter)
{
int wb = 0, werr = 0;
struct part_iter *iter = NULL;
- struct disk_dos_part_entry *dp;
int ridx;
if (!opt.fixchs)
return 0;
if (miter->type != typedos) {
- error("Options 'fixchs' is meaningful only for legacy partition scheme.\n");
+ error("Option 'fixchs' works only for legacy (DOS) partition scheme.");
return -1;
}
- if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */
+ if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
return -1;
- while (!pi_next(&iter) && !werr) {
- ridx = iter->rawindex;
- dp = (struct disk_dos_part_entry *)iter->record;
+ while (!pi_next(iter) && !werr) {
+ ridx = iter->index0;
- wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
- if (ridx > 4)
- wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
+ wb |= updchs(iter, 0);
+ if (ridx > 3)
+ wb |= updchs(iter, 1);
- if (ridx >= 4 && wb && !werr) {
- push_embr(miter, iter);
- werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ /*
+ * we have to update mbr and each extended partition, but only if
+ * changes (wb) were detected and there was no prior write error (werr)
+ */
+ if (ridx >= 3 && wb && !werr) {
+ mbrcpy(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
wb = 0;
}
}
- if (iter->status > PI_DONE)
+ if (pi_errored(iter))
goto bail;
- /* last write */
+ /* last update */
if (wb && !werr) {
- push_embr(miter, iter);
- werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ mbrcpy(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
}
if (werr)
- error("WARNING: failed to write E/MBR during 'fixchs'\n");
+ warn("Failed to write E/MBR during 'fixchs'.");
bail:
pi_del(&iter);
diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h
index bcefea3b..d4a5b759 100644
--- a/com32/chain/mangle.h
+++ b/com32/chain/mangle.h
@@ -1,5 +1,35 @@
-#ifndef _COM32_CHAIN_MANGLE_H
-#define _COM32_CHAIN_MANGLE_H
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_MANGLE_H
+#define COM32_CHAIN_MANGLE_H
#include "chain.h"
#include "partiter.h"
diff --git a/com32/chain/options.c b/com32/chain/options.c
index 658a45ca..4e722a01 100644
--- a/com32/chain/options.c
+++ b/com32/chain/options.c
@@ -1,21 +1,55 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <syslinux/movebits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include "common.h"
#include "chain.h"
+#include "partiter.h"
#include "utility.h"
#include "options.h"
struct options opt;
-static int soi_s2n(char *ptr, unsigned int *seg,
- unsigned int *off,
- unsigned int *ip,
- unsigned int def)
+static int soi_s2n(char *ptr,
+ addr_t *seg,
+ addr_t *off,
+ addr_t *ip,
+ addr_t def)
{
- unsigned int segval = 0, offval, ipval, val;
+ addr_t segval, offval, ipval, val;
char *p;
+ /* defaults */
+ segval = 0;
offval = def;
ipval = def;
@@ -25,17 +59,22 @@ static int soi_s2n(char *ptr, unsigned int *seg,
if (p[0] == ':' && p[1] && p[1] != ':')
ipval = strtoul(p+1, NULL, 0);
+ /* verify if load address is within [dosmin, dosmax) */
val = (segval << 4) + offval;
- if (val < ADDRMIN || val > ADDRMAX) {
- error("Invalid seg:off:* address specified..\n");
+ if (val < dosmin || val >= dosmax) {
+ error("Invalid seg:off:* address specified.");
goto bail;
}
+ /*
+ * verify if jump address is within [dosmin, dosmax) and offset is 16bit
+ * sane
+ */
val = (segval << 4) + ipval;
- if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
- error("Invalid seg:*:ip address specified.\n");
+ if (ipval > 0xFFFE || val < dosmin || val >= dosmax) {
+ error("Invalid seg:*:ip address specified.");
goto bail;
}
@@ -53,75 +92,82 @@ bail:
static void usage(void)
{
- unsigned int i;
- static const char key[] = "Press any key...\n";
+ size_t i;
static const char *const usage[] = {
-"\
-Usage:\n\
- chain.c32 [options]\n\
- chain.c32 {fd|hd}<disk#>{,| }[<part#>] [options]\n\
- chain.c32 mbr{:|=}<id>{,| }[<part#>] [options]\n\
- chain.c32 guid{:|=}<guid>{,| }[<part#>] [options]\n\
- chain.c32 label{:|=}<label> [<part#>] [options]\n\
- chain.c32 boot{,| }[<part#>] [options]\n\
- chain.c32 fs [options]\n\
-", "\
-\nOptions ('no' prefix specifies default value):\n\
- sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\
- - defaults to 0:0x7C00:0x7C00\n\
- - ommited o/i values default to 0\n\
- maps Map loaded sector into real memory\n\
- nosetbpb Fix BPB fields in loaded sector\n\
- nofilebpb Apply 'setbpb' to loaded file\n\
- nosave Write adjusted sector back to disk\n\
- hand Prepare handover area\n\
- nohptr Force ds:si and ds:bp to point to handover area\n\
- noswap Swap drive numbers, if bootdisk is not fd0/hd0\n\
- nohide Disable all hide variations (also the default)\n\
- hide Hide primary partitions, unhide selected partition\n\
- hideall Hide *all* partitions, unhide selected partition\n\
- unhide Unhide primary partitions\n\
- unhideall Unhide *all* partitions\n\
- nofixchs Walk *all* partitions and fix E/MBRs' chs values\n\
- nokeeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
- nowarn Wait for a keypress to continue chainloading\n\
- - useful to see emited warnings\n\
- nobreak Actually perform the chainloading\n\
-", "\
-\nOptions continued ...\n\
- file=<file> Load and execute <file>\n\
- seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\
- - defaults to 0:0x7C00:0x7C00\n\
- - ommited o/i values default to 0\n\
- isolinux=<loader> Load another version of ISOLINUX\n\
- ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
- reactos=<loader> Load ReactOS's loader\n\
- cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
- freedos=<loader> Load FreeDOS KERNEL.SYS\n\
- msdos=<loader> Load MS-DOS 2.xx - 6.xx IO.SYS\n\
- msdos7=<loader> Load MS-DOS 7+ IO.SYS\n\
- pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
- drmk=<loader> Load DRMK DELLBIO.BIN\n\
- grub=<loader> Load GRUB Legacy stage2\n\
- grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
- grldr=<loader> Load GRUB4DOS grldr\n\
- bss=<filename> Emulate syslinux's BSS\n\
- bs=<filename> Emulate syslinux's BS\n\
-\nPlease see doc/chain.txt for the detailed documentation.\n\
-"
- };
+"Usage:",
+"",
+" disk + partition selection:",
+" chain.c32 [options]",
+" chain.c32 hd#[,#] [options]",
+" chain.c32 fd#[,#] [options]",
+" chain.c32 mbr=<id>[,#] [options]",
+" chain.c32 guid=<guid>[,#] [options]",
+" chain.c32 boot[,#] [options]",
+"",
+" direct partition selection:",
+" chain.c32 guid=<guid> [options]",
+" chain.c32 label=<label> [options]",
+" chain.c32 fs [options]",
+"",
+"You can use ':' instead of '=' and ' ' instead of ','.",
+"The default is 'boot,0'.",
+"",
+"Options:",
+" sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>",
+" - defaults to 0:0x7C00:0x7C00",
+" - omitted o/i values default to 0",
+" maps Map loaded sector into real memory",
+" setbpb Fix BPB fields in loaded sector",
+" filebpb Apply 'setbpb' to loaded file",
+" save Write adjusted sector back to disk",
+" hand Prepare handover area",
+" hptr Force ds:si and ds:bp to point to handover area",
+" swap Swap drive numbers, if bootdisk is not fd0/hd0",
+" nohide Disable all hide variations (default)",
+" hide Hide primary partitions, unhide selected partition",
+" hideall Hide *all* partitions, unhide selected partition",
+" unhide Unhide primary partitions",
+" unhideall Unhide *all* partitions",
+" fixchs Walk *all* partitions and fix E/MBRs' CHS values",
+" keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)",
+" warn Wait for a keypress to continue chainloading",
+" break Don't chainload",
+" relax Relax sanity checks",
+" prefmbr On hybrid MBR/GPT disks, prefer legacy layout",
+"",
+" file=<file> Load and execute <file>",
+" seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>",
+" - defaults to 0:0x7C00:0x7C00",
+" - omitted o/i values default to 0",
+" isolinux=<loader> Load another version of ISOLINUX",
+" ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR",
+" reactos=<loader> Load ReactOS's loader",
+" cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003",
+" freedos=<loader> Load FreeDOS KERNEL.SYS",
+" msdos=<loader> Load MS-DOS 2.xx - 6.xx IO.SYS",
+" msdos7=<loader> Load MS-DOS 7+ IO.SYS",
+" pcdos=<loader> Load PC-DOS IBMBIO.COM",
+" drmk=<loader> Load DRMK DELLBIO.BIN",
+" grub=<loader> Load GRUB Legacy stage2",
+" grubcfg=<config> Set alternative config filename for GRUB Legacy",
+" grldr=<loader> Load GRUB4DOS grldr",
+" bss=<sectimage> Emulate syslinux's BSS",
+" bs=<sectimage> Emulate syslinux's BS",
+"",
+"Please see doc/chain.txt for the detailed documentation."
+};
for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) {
- if (i) {
- error(key);
+ if (i % 20 == 19) {
+ puts("Press any key...");
wait_key();
}
- error(usage[i]);
+ puts(usage[i]);
}
}
void opt_set_defs(void)
{
- memset(&opt, 0, sizeof(opt));
+ memset(&opt, 0, sizeof opt);
opt.sect = true; /* by def. load sector */
opt.maps = true; /* by def. map sector */
opt.hand = true; /* by def. prepare handover */
@@ -136,7 +182,7 @@ void opt_set_defs(void)
int opt_parse_args(int argc, char *argv[])
{
int i;
- unsigned int v;
+ size_t v;
char *p;
for (i = 1; i < argc; i++) {
@@ -152,7 +198,6 @@ int opt_parse_args(int argc, char *argv[])
opt.bss = true;
opt.maps = false;
opt.setbpb = true;
- /* opt.save = true; */
} else if (!strncmp(argv[i], "bs=", 3)) {
opt.file = argv[i] + 3;
opt.sect = false;
@@ -168,7 +213,6 @@ int opt_parse_args(int argc, char *argv[])
opt.fip = 0;
opt.file = argv[i] + 6;
opt.setbpb = true;
- /* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "reactos=", 8)) {
/*
@@ -182,7 +226,6 @@ int opt_parse_args(int argc, char *argv[])
opt.fip = 0x8100;
opt.file = argv[i] + 8;
opt.setbpb = true;
- /* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "cmldr=", 6)) {
opt.fseg = 0x2000; /* CMLDR wants this address */
@@ -191,7 +234,6 @@ int opt_parse_args(int argc, char *argv[])
opt.file = argv[i] + 6;
opt.cmldr = true;
opt.setbpb = true;
- /* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "freedos=", 8)) {
opt.fseg = 0x60; /* FREEDOS wants this address */
@@ -200,7 +242,6 @@ int opt_parse_args(int argc, char *argv[])
opt.sseg = 0x1FE0;
opt.file = argv[i] + 8;
opt.setbpb = true;
- /* opt.save = true; */
opt.hand = false;
} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
!strncmp(argv[i], "pcdos=", v)) ||
@@ -211,7 +252,6 @@ int opt_parse_args(int argc, char *argv[])
opt.sseg = 0x8000;
opt.file = argv[i] + v;
opt.setbpb = true;
- /* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "drmk=", 5)) {
opt.fseg = 0x70; /* DRMK wants this address */
@@ -223,7 +263,6 @@ int opt_parse_args(int argc, char *argv[])
opt.file = argv[i] + 5;
/* opt.drmk = true; */
opt.setbpb = true;
- /* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "grub=", 5)) {
opt.fseg = 0x800; /* stage2 wants this address */
@@ -261,15 +300,15 @@ int opt_parse_args(int argc, char *argv[])
} else if (!strcmp(argv[i], "noswap")) {
opt.swap = false;
} else if (!strcmp(argv[i], "nohide")) {
- opt.hide = 0;
+ opt.hide = HIDE_OFF;
} else if (!strcmp(argv[i], "hide")) {
- opt.hide = 1; /* 001b */
+ opt.hide = HIDE_ON;
} else if (!strcmp(argv[i], "hideall")) {
- opt.hide = 2; /* 010b */
+ opt.hide = HIDE_ON | HIDE_EXT;
} else if (!strcmp(argv[i], "unhide")) {
- opt.hide = 5; /* 101b */
+ opt.hide = HIDE_ON | HIDE_REV;
} else if (!strcmp(argv[i], "unhideall")) {
- opt.hide = 6; /* 110b */
+ opt.hide = HIDE_ON | HIDE_EXT | HIDE_REV;
} else if (!strcmp(argv[i], "setbpb")) {
opt.setbpb = true;
} else if (!strcmp(argv[i], "nosetbpb")) {
@@ -296,10 +335,18 @@ int opt_parse_args(int argc, char *argv[])
opt.fixchs = true;
} else if (!strcmp(argv[i], "nofixchs")) {
opt.fixchs = false;
+ } else if (!strcmp(argv[i], "relax")) {
+ opt.piflags |= PIF_RELAX;
+ } else if (!strcmp(argv[i], "norelax")) {
+ opt.piflags &= ~PIF_RELAX;
} else if (!strcmp(argv[i], "warn")) {
opt.warn = true;
} else if (!strcmp(argv[i], "nowarn")) {
opt.warn = false;
+ } else if (!strcmp(argv[i], "prefmbr")) {
+ opt.piflags |= PIF_PREFMBR;
+ } else if (!strcmp(argv[i], "noprefmbr")) {
+ opt.piflags &= ~PIF_PREFMBR;
} else if (!strcmp(argv[i], "nobreak")) {
opt.brkchain = false;
} else if (!strcmp(argv[i], "break")) {
@@ -337,34 +384,27 @@ int opt_parse_args(int argc, char *argv[])
}
if (opt.grubcfg && !opt.grub) {
- error("grubcfg=<filename> must be used together with grub=<loader>.\n");
+ error("grubcfg=<filename> must be used together with grub=<loader>.");
goto bail;
}
-#if 0
- if ((!opt.maps || !opt.sect) && !opt.file) {
- error("You have to load something.\n");
- goto bail;
- }
-#endif
-
if (opt.filebpb && !opt.file) {
- error("Option 'filebpb' requires a file.\n");
+ error("Option 'filebpb' requires a file.");
goto bail;
}
if (opt.save && !opt.sect) {
- error("Option 'save' requires a sector.\n");
+ error("Option 'save' requires a sector.");
goto bail;
}
if (opt.setbpb && !opt.sect) {
- error("Option 'setbpb' requires a sector.\n");
+ error("Option 'setbpb' requires a sector.");
goto bail;
}
if (opt.maps && !opt.sect) {
- error("Option 'maps' requires a sector.\n");
+ error("Option 'maps' requires a sector.");
goto bail;
}
diff --git a/com32/chain/options.h b/com32/chain/options.h
index 4493ef1f..df96e2d3 100644
--- a/com32/chain/options.h
+++ b/com32/chain/options.h
@@ -1,20 +1,56 @@
-#ifndef _COM32_CHAIN_OPTIONS_H
-#define _COM32_CHAIN_OPTIONS_H
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_OPTIONS_H
+#define COM32_CHAIN_OPTIONS_H
#include <stdint.h>
#include <syslinux/bootrm.h>
+#include <syslinux/movebits.h>
+
+enum {HIDE_OFF = 0, HIDE_ON = 1, HIDE_EXT = 2, HIDE_REV = 4};
struct options {
- unsigned int fseg;
- unsigned int foff;
- unsigned int fip;
- unsigned int sseg;
- unsigned int soff;
- unsigned int sip;
const char *drivename;
const char *partition;
const char *file;
const char *grubcfg;
+ addr_t fseg;
+ addr_t foff;
+ addr_t fip;
+ addr_t sseg;
+ addr_t soff;
+ addr_t sip;
+ int hide;
+ int piflags;
+ uint16_t keeppxe;
bool isolinux;
bool cmldr;
bool drmk;
@@ -24,7 +60,6 @@ struct options {
bool hand;
bool hptr;
bool swap;
- int hide;
bool sect;
bool save;
bool bss;
@@ -33,7 +68,6 @@ struct options {
bool fixchs;
bool warn;
bool brkchain;
- uint16_t keeppxe;
struct syslinux_rm_regs regs;
};
diff --git a/com32/chain/partiter.c b/com32/chain/partiter.c
index 1acd1958..1eb5350d 100644
--- a/com32/chain/partiter.c
+++ b/com32/chain/partiter.c
@@ -1,8 +1,9 @@
/* ----------------------------------------------------------------------- *
*
- * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
* Copyright 2010 Shao Miller
- * Copyright 2010 Michal Soltys
+ * Copyright 2010-2012 Michal Soltys
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -39,7 +40,6 @@
#include <stdarg.h>
#include <zlib.h>
#include <syslinux/disk.h>
-#include "common.h"
#include "partiter.h"
#include "utility.h"
@@ -47,175 +47,110 @@
#define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00)
#define sane(s,l) ((s)+(l) > (s))
-/* forwards */
+/* virtual forwards */
-static int iter_ctor(struct part_iter *, va_list *);
-static int iter_dos_ctor(struct part_iter *, va_list *);
-static int iter_gpt_ctor(struct part_iter *, va_list *);
-static void iter_dtor(struct part_iter *);
-static struct part_iter *pi_dos_next(struct part_iter *);
-static struct part_iter *pi_gpt_next(struct part_iter *);
-static struct part_iter *pi_raw_next(struct part_iter *);
+static void pi_dtor_(struct part_iter *);
+static int pi_next_(struct part_iter *);
+static int pi_dos_next(struct part_iter *);
+static int pi_gpt_next(struct part_iter *);
+
+/* vtab and types */
static struct itertype types[] = {
[0] = {
- .ctor = &iter_dos_ctor,
- .dtor = &iter_dtor,
+ .dtor = &pi_dtor_,
.next = &pi_dos_next,
}, [1] = {
- .ctor = &iter_gpt_ctor,
- .dtor = &iter_dtor,
+ .dtor = &pi_dtor_,
.next = &pi_gpt_next,
}, [2] = {
- .ctor = &iter_ctor,
- .dtor = &iter_dtor,
- .next = &pi_raw_next,
+ .dtor = &pi_dtor_,
+ .next = &pi_next_,
}};
const struct itertype * const typedos = types;
const struct itertype * const typegpt = types+1;
const struct itertype * const typeraw = types+2;
-#ifdef DEBUG
-static int inv_type(const void *type)
+/* pi_dtor_() - common/raw iterator cleanup */
+static void pi_dtor_(struct part_iter *iter)
{
- int i, cnt = sizeof(types)/sizeof(types[0]);
- for (i = 0; i < cnt; i++) {
- if (type == types + i)
- return 0;
- }
- return -1;
+ /* syslinux's free is null resilient */
+ free(iter->data);
}
-#endif
-/**
- * iter_ctor() - common iterator initialization
- * @iter: iterator pointer
- * @args(0): disk_info structure used for disk functions
- * @args(1): stepall modifier
- *
- * Second and further arguments are passed as a pointer to va_list
- **/
-static int iter_ctor(struct part_iter *iter, va_list *args)
+/* pi_ctor() - common/raw iterator initialization */
+static int pi_ctor(struct part_iter *iter,
+ const struct disk_info *di, int flags
+)
{
- const struct disk_info *di = va_arg(*args, const struct disk_info *);
- int stepall = va_arg(*args, int);
-
-#ifdef DEBUG
- if (!di)
- return -1;
-#endif
-
- memcpy(&iter->di, di, sizeof(struct disk_info));
- iter->stepall = stepall;
+ memcpy(&iter->di, di, sizeof *di);
+ iter->flags = flags;
iter->index0 = -1;
iter->length = di->lbacnt;
+ iter->type = typeraw;
return 0;
}
-/**
- * iter_dtor() - common iterator cleanup
- * @iter: iterator pointer
- *
- **/
-static void iter_dtor(struct part_iter *iter)
+/* pi_dos_ctor() - MBR/EBR iterator specific initialization */
+static int pi_dos_ctor(struct part_iter *iter,
+ const struct disk_info *di, int flags,
+ const struct disk_dos_mbr *mbr
+)
{
- free(iter->data);
-}
-
-/**
- * iter_dos_ctor() - MBR/EBR iterator specific initialization
- * @iter: iterator pointer
- * @args(0): disk_info structure used for disk functions
- * @args(1): pointer to buffer with loaded valid MBR
- *
- * Second and further arguments are passed as a pointer to va_list.
- * This function only makes rudimentary checks. If user uses
- * pi_new(), he/she is responsible for doing proper sanity checks.
- **/
-static int iter_dos_ctor(struct part_iter *iter, va_list *args)
-{
- const struct disk_dos_mbr *mbr;
-
- /* uses args(0) */
- if (iter_ctor(iter, args))
+ if (pi_ctor(iter, di, flags))
return -1;
- mbr = va_arg(*args, const struct disk_dos_mbr *);
-
-#ifdef DEBUG
- if (!mbr)
- goto bail;
-#endif
-
- if (!(iter->data = malloc(sizeof(struct disk_dos_mbr))))
+ if (!(iter->data = malloc(sizeof *mbr))) {
+ critm();
goto bail;
+ }
- memcpy(iter->data, mbr, sizeof(struct disk_dos_mbr));
+ memcpy(iter->data, mbr, sizeof *mbr);
- iter->sub.dos.bebr_index0 = -1;
- iter->sub.dos.disk_sig = mbr->disk_sig;
+ iter->dos.bebr_index0 = -1;
+ iter->dos.disk_sig = mbr->disk_sig;
+ iter->type = typedos;
return 0;
bail:
- iter->type->dtor(iter);
+ pi_dtor_(iter);
return -1;
}
-/**
- * iter_gpt_ctor() - GPT iterator specific initialization
- * @iter: iterator pointer
- * @args(0): ptr to disk_info structure
- * @args(1): ptr to buffer with GPT header
- * @args(2): ptr to buffer with GPT partition list
- *
- * Second and further arguments are passed as a pointer to va_list.
- * This function only makes rudimentary checks. If user uses
- * pi_new(), he/she is responsible for doing proper sanity checks.
- **/
-static int iter_gpt_ctor(struct part_iter *iter, va_list *args)
+/* pi_gpt_ctor() - GPT iterator specific initialization */
+static int pi_gpt_ctor(struct part_iter *iter,
+ const struct disk_info *di, int flags,
+ const struct disk_gpt_header *gpth, const struct disk_gpt_part_entry *gptl
+)
{
uint64_t siz;
- const struct disk_gpt_header *gpth;
- const struct disk_gpt_part_entry *gptl;
- /* uses args(0) */
- if (iter_ctor(iter, args))
+ if (pi_ctor(iter, di, flags))
return -1;
- gpth = va_arg(*args, const struct disk_gpt_header *);
- gptl = va_arg(*args, const struct disk_gpt_part_entry *);
-
-#ifdef DEBUG
- if (!gpth || !gptl)
- goto bail;
-#endif
-
siz = (uint64_t)gpth->part_count * gpth->part_size;
-#ifdef DEBUG
- if (!siz || (siz + iter->di.bps - 1) / iter->di.bps > 255u ||
- gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
+ if (!(iter->data = malloc((size_t)siz))) {
+ critm();
goto bail;
}
-#endif
-
- if (!(iter->data = malloc((size_t)siz)))
- goto bail;
memcpy(iter->data, gptl, (size_t)siz);
- iter->sub.gpt.pe_count = (int)gpth->part_count;
- iter->sub.gpt.pe_size = (int)gpth->part_size;
- iter->sub.gpt.ufirst = gpth->lba_first_usable;
- iter->sub.gpt.ulast = gpth->lba_last_usable;
+ iter->gpt.pe_count = (int)gpth->part_count;
+ iter->gpt.pe_size = (int)gpth->part_size;
+ iter->gpt.ufirst = gpth->lba_first_usable;
+ iter->gpt.ulast = gpth->lba_last_usable;
- memcpy(&iter->sub.gpt.disk_guid, &gpth->disk_guid, sizeof(struct guid));
+ memcpy(&iter->gpt.disk_guid, &gpth->disk_guid, sizeof gpth->disk_guid);
+ memcpy(&iter->gpt.part_guid, &gpth->disk_guid, sizeof gpth->disk_guid);
+ iter->type = typegpt;
return 0;
bail:
- iter->type->dtor(iter);
+ pi_dtor_(iter);
return -1;
}
@@ -237,18 +172,21 @@ static int notsane_logical(const struct part_iter *iter)
return 0;
if (ost_is_ext(dp[0].ostype)) {
- error("1st EBR entry must be data or empty.\n");
+ error("The 1st EBR entry must be data or empty.");
return -1;
}
+ if (iter->flags & PIF_RELAX)
+ return 0;
+
end_log = dp[0].start_lba + dp[0].length;
if (!dp[0].start_lba ||
!dp[0].length ||
!sane(dp[0].start_lba, dp[0].length) ||
- end_log > iter->sub.dos.ebr_size) {
+ end_log > iter->dos.nebr_siz) {
- error("Insane logical partition.\n");
+ error("Logical partition (in EBR) with invalid offset and/or length.");
return -1;
}
@@ -273,18 +211,21 @@ static int notsane_extended(const struct part_iter *iter)
return 0;
if (!ost_is_nondata(dp[1].ostype)) {
- error("2nd EBR entry must be extended or empty.\n");
+ error("The 2nd EBR entry must be extended or empty.");
return -1;
}
+ if (iter->flags & PIF_RELAX)
+ return 0;
+
end_ebr = dp[1].start_lba + dp[1].length;
if (!dp[1].start_lba ||
!dp[1].length ||
!sane(dp[1].start_lba, dp[1].length) ||
- end_ebr > iter->sub.dos.bebr_size) {
+ end_ebr > iter->dos.bebr_siz) {
- error("Insane extended partition.\n");
+ error("Extended partition (EBR) with invalid offset and/or length.");
return -1;
}
@@ -304,11 +245,14 @@ static int notsane_primary(const struct part_iter *iter)
if (!dp->ostype)
return 0;
+ if (iter->flags & PIF_RELAX)
+ return 0;
+
if (!dp->start_lba ||
!dp->length ||
!sane(dp->start_lba, dp->length) ||
dp->start_lba + dp->length > iter->di.lbacnt) {
- error("Insane primary (MBR) partition.\n");
+ error("Primary partition (in MBR) with invalid offset and/or length.");
return -1;
}
@@ -319,21 +263,24 @@ static int notsane_gpt(const struct part_iter *iter)
{
const struct disk_gpt_part_entry *gp;
gp = (const struct disk_gpt_part_entry *)
- (iter->data + iter->index0 * iter->sub.gpt.pe_size);
+ (iter->data + iter->index0 * iter->gpt.pe_size);
if (guid_is0(&gp->type))
return 0;
- if (gp->lba_first < iter->sub.gpt.ufirst ||
- gp->lba_last > iter->sub.gpt.ulast) {
- error("Insane GPT partition.\n");
+ if (iter->flags & PIF_RELAX)
+ return 0;
+
+ if (gp->lba_first < iter->gpt.ufirst ||
+ gp->lba_last > iter->gpt.ulast) {
+ error("LBA sectors of GPT partition are beyond the range allowed in GPT header.");
return -1;
}
return 0;
}
-static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
+static int dos_next_mbr(struct part_iter *iter, uint32_t *lba,
struct disk_dos_part_entry **_dp)
{
struct disk_dos_part_entry *dp;
@@ -343,19 +290,19 @@ static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
if (notsane_primary(iter)) {
iter->status = PI_INSANE;
- goto bail;
+ return -1;
}
if (ost_is_ext(dp->ostype)) {
- if (iter->sub.dos.bebr_index0 >= 0) {
- error("You have more than 1 extended partition.\n");
+ if (iter->dos.bebr_index0 >= 0) {
+ error("More than 1 extended partition.");
iter->status = PI_INSANE;
- goto bail;
+ return -1;
}
/* record base EBR index */
- iter->sub.dos.bebr_index0 = iter->index0;
+ iter->dos.bebr_index0 = iter->index0;
}
- if (!ost_is_nondata(dp->ostype) || iter->stepall) {
+ if (!ost_is_nondata(dp->ostype) || (iter->flags & PIF_STEPALL)) {
*lba = dp->start_lba;
*_dp = dp;
break;
@@ -363,52 +310,48 @@ static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
}
return 0;
-bail:
- return -1;
}
static int prep_base_ebr(struct part_iter *iter)
{
struct disk_dos_part_entry *dp;
- if (iter->sub.dos.bebr_index0 < 0) /* if we don't have base extended partition at all */
+ if (iter->dos.bebr_index0 < 0) /* if we don't have base extended partition at all */
return -1;
- else if (!iter->sub.dos.bebr_start) { /* if not initialized yet */
- dp = ((struct disk_dos_mbr *)iter->data)->table + iter->sub.dos.bebr_index0;
-
- iter->sub.dos.bebr_start = dp->start_lba;
- iter->sub.dos.bebr_size = dp->length;
+ else if (!iter->dos.bebr_lba) { /* if not initialized yet */
+ dp = ((struct disk_dos_mbr *)iter->data)->table + iter->dos.bebr_index0;
- iter->sub.dos.ebr_start = 0;
- iter->sub.dos.ebr_size = iter->sub.dos.bebr_size;
+ iter->dos.bebr_lba = dp->start_lba;
+ iter->dos.bebr_siz = dp->length;
- iter->sub.dos.cebr_lba = 0;
- iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start;
+ iter->dos.nebr_lba = dp->start_lba;
+ iter->dos.nebr_siz = dp->length;
iter->index0--;
}
return 0;
}
-static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
+static int dos_next_ebr(struct part_iter *iter, uint32_t *lba,
struct disk_dos_part_entry **_dp)
{
struct disk_dos_part_entry *dp;
- if (prep_base_ebr(iter)) {
+ if (prep_base_ebr(iter) < 0) {
iter->status = PI_DONE;
return -1;
}
- while (++iter->index0 < 1024 && iter->sub.dos.nebr_lba) {
+ while (++iter->index0 < 1024 && iter->dos.nebr_lba) {
free(iter->data);
if (!(iter->data =
- disk_read_sectors(&iter->di, iter->sub.dos.nebr_lba, 1))) {
- error("Couldn't load EBR.\n");
+ disk_read_sectors(&iter->di, iter->dos.nebr_lba, 1))) {
+ error("Couldn't load EBR.");
iter->status = PI_ERRLOAD;
return -1;
}
+ /* check sanity of loaded data */
if (notsane_logical(iter) || notsane_extended(iter)) {
iter->status = PI_INSANE;
return -1;
@@ -416,24 +359,23 @@ static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
dp = ((struct disk_dos_mbr *)iter->data)->table;
- iter->sub.dos.cebr_lba = iter->sub.dos.nebr_lba;
+ iter->dos.cebr_lba = iter->dos.nebr_lba;
+ iter->dos.cebr_siz = iter->dos.nebr_siz;
/* setup next frame values */
if (dp[1].ostype) {
- iter->sub.dos.ebr_start = dp[1].start_lba;
- iter->sub.dos.ebr_size = dp[1].length;
- iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start + dp[1].start_lba;
+ iter->dos.nebr_lba = iter->dos.bebr_lba + dp[1].start_lba;
+ iter->dos.nebr_siz = dp[1].length;
} else {
- iter->sub.dos.ebr_start = 0;
- iter->sub.dos.ebr_size = 0;
- iter->sub.dos.nebr_lba = 0;
+ iter->dos.nebr_lba = 0;
+ iter->dos.nebr_siz = 0;
}
if (!dp[0].ostype)
- iter->sub.dos.skipcnt++;
+ iter->dos.logskipcnt++;
- if (dp[0].ostype || iter->stepall) {
- *lba = iter->sub.dos.cebr_lba + dp[0].start_lba;
+ if (dp[0].ostype || (iter->flags & PIF_STEPALL)) {
+ *lba = dp[0].start_lba ? iter->dos.cebr_lba + dp[0].start_lba : 0;
*_dp = dp;
return 0;
}
@@ -441,128 +383,33 @@ static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
* This way it's possible to continue, if some crazy soft left a "hole"
* - EBR with a valid extended partition without a logical one. In
* such case, linux will not reserve a number for such hole - so we
- * don't increase index0. If stepall flag is set, we will never reach
- * this place.
+ * don't increase index0. If PIF_STEPALL flag is set, we will never
+ * reach this place.
*/
}
iter->status = PI_DONE;
return -1;
}
-static struct part_iter *pi_dos_next(struct part_iter *iter)
-{
- uint32_t start_lba = 0;
- struct disk_dos_part_entry *dos_part = NULL;
-
- if (iter->status)
- goto bail;
-
- /* look for primary partitions */
- if (iter->index0 < 4 &&
- pi_dos_next_mbr(iter, &start_lba, &dos_part))
- goto bail;
-
- /* look for logical partitions */
- if (iter->index0 >= 4 &&
- pi_dos_next_ebr(iter, &start_lba, &dos_part))
- goto bail;
-
- /*
- * note special index handling, if we have stepall set -
- * this is made to keep index consistent with non-stepall
- * iterators
- */
-
- if (iter->index0 >= 4 && !dos_part->ostype)
- iter->index = -1;
- else
- iter->index = iter->index0 - iter->sub.dos.skipcnt + 1;
- iter->rawindex = iter->index0 + 1;
- iter->start_lba = start_lba;
- iter->length = dos_part->length;
- iter->record = (char *)dos_part;
-
-#ifdef DEBUG
- disk_dos_part_dump(dos_part);
-#endif
-
- return iter;
-bail:
- return NULL;
-}
-
static void gpt_conv_label(struct part_iter *iter)
{
const struct disk_gpt_part_entry *gp;
const int16_t *orig_lab;
gp = (const struct disk_gpt_part_entry *)
- (iter->data + iter->index0 * iter->sub.gpt.pe_size);
+ (iter->data + iter->index0 * iter->gpt.pe_size);
orig_lab = (const int16_t *)gp->name;
/* caveat: this is very crude conversion */
for (int i = 0; i < PI_GPTLABSIZE/2; i++) {
- iter->sub.gpt.part_label[i] = (char)orig_lab[i];
- }
- iter->sub.gpt.part_label[PI_GPTLABSIZE/2] = 0;
-}
-
-static struct part_iter *pi_gpt_next(struct part_iter *iter)
-{
- const struct disk_gpt_part_entry *gpt_part = NULL;
-
- if (iter->status)
- goto bail;
-
- while (++iter->index0 < iter->sub.gpt.pe_count) {
- gpt_part = (const struct disk_gpt_part_entry *)
- (iter->data + iter->index0 * iter->sub.gpt.pe_size);
-
- if (notsane_gpt(iter)) {
- iter->status = PI_INSANE;
- goto bail;
- }
-
- if (!guid_is0(&gpt_part->type) || iter->stepall)
- break;
- }
- /* no more partitions ? */
- if (iter->index0 == iter->sub.gpt.pe_count) {
- iter->status = PI_DONE;
- goto bail;
+ iter->gpt.part_label[i] = (char)orig_lab[i];
}
- /* gpt_part is guaranteed to be valid here */
- iter->index = iter->index0 + 1;
- iter->rawindex = iter->index0 + 1;
- iter->start_lba = gpt_part->lba_first;
- iter->length = gpt_part->lba_last - gpt_part->lba_first + 1;
- iter->record = (char *)gpt_part;
- memcpy(&iter->sub.gpt.part_guid, &gpt_part->uid, sizeof(struct guid));
- gpt_conv_label(iter);
-
-#ifdef DEBUG
- disk_gpt_part_dump(gpt_part);
-#endif
-
- return iter;
-bail:
- return NULL;
-}
-
-static struct part_iter *pi_raw_next(struct part_iter *iter)
-{
- iter->status = PI_DONE;
- return NULL;
+ iter->gpt.part_label[PI_GPTLABSIZE/2] = 0;
}
-static int check_crc(uint32_t crc_match, const uint8_t *buf, unsigned int siz)
+static inline int valid_crc(uint32_t crc, const uint8_t *buf, unsigned int siz)
{
- uint32_t crc;
-
- crc = crc32(0, NULL, 0);
- crc = crc32(crc, buf, siz);
-
- return crc_match != crc;
+ return crc == crc32(crc32(0, NULL, 0), buf, siz);
}
static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct disk_gpt_header **_gh)
@@ -573,19 +420,19 @@ static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct dis
hold_crc32 = gh->chksum;
gh->chksum = 0;
- if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
- error("WARNING: Primary GPT header checksum invalid.\n");
+ if (!valid_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
+ warn("Primary GPT header checksum invalid.");
/* retry with backup */
lba_alt = gh->lba_alt;
free(gh);
if (!(gh = *_gh = disk_read_sectors(diskinfo, lba_alt, 1))) {
- error("Couldn't read backup GPT header.\n");
+ error("Couldn't read backup GPT header.");
return -1;
}
hold_crc32 = gh->chksum;
gh->chksum = 0;
- if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
- error("Secondary GPT header checksum invalid.\n");
+ if (!valid_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
+ error("Secondary GPT header checksum invalid.");
return -1;
}
}
@@ -595,149 +442,156 @@ static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct dis
return 0;
}
-/*
- * ----------------------------------------------------------------------------
- * Following functions are for users to call.
- * ----------------------------------------------------------------------------
- */
-
+static int pi_next_(struct part_iter *iter)
+{
+ iter->status = PI_DONE;
+ return iter->status;
+}
-int pi_next(struct part_iter **_iter)
+static int pi_dos_next(struct part_iter *iter)
{
- struct part_iter *iter;
+ uint32_t abs_lba = 0;
+ struct disk_dos_part_entry *dos_part = NULL;
+
+ if (iter->status)
+ return iter->status;
+
+ /* look for primary partitions */
+ if (iter->index0 < 4 &&
+ dos_next_mbr(iter, &abs_lba, &dos_part) < 0)
+ return iter->status;
+
+ /* look for logical partitions */
+ if (iter->index0 >= 4 &&
+ dos_next_ebr(iter, &abs_lba, &dos_part) < 0)
+ return iter->status;
+
+ /*
+ * note special index handling:
+ * in case PIF_STEPALL is set - this makes the index consistent with
+ * non-PIF_STEPALL iterators
+ */
+
+ if (!dos_part->ostype)
+ iter->index = -1;
+ else
+ iter->index = iter->index0 + 1 - iter->dos.logskipcnt;
+ iter->abs_lba = abs_lba;
+ iter->length = dos_part->length;
+ iter->record = (char *)dos_part;
- if(!_iter || !*_iter)
- return 0;
- iter = *_iter;
#ifdef DEBUG
- if (inv_type(iter->type)) {
- error("This is not a valid iterator.\n");
- return 0;
- }
+ disk_dos_part_dump(dos_part);
#endif
- if ((iter = iter->type->next(iter))) {
- *_iter = iter;
- }
- return (*_iter)->status;
+
+ return iter->status;
}
-/**
- * pi_new() - get new iterator
- * @itertype: iterator type
- * @...: variable arguments passed to ctors
- *
- * Variable arguments depend on the type. Please see functions:
- * iter_gpt_ctor() and iter_dos_ctor() for details.
- **/
-struct part_iter *pi_new(const struct itertype *type, ...)
+static int pi_gpt_next(struct part_iter *iter)
{
- int badctor = 0;
- struct part_iter *iter = NULL;
- va_list ap;
+ const struct disk_gpt_part_entry *gpt_part = NULL;
- va_start(ap, type);
+ if (iter->status)
+ return iter->status;
-#ifdef DEBUG
- if (inv_type(type)) {
- error("Unknown iterator requested.\n");
- goto bail;
- }
-#endif
+ while (++iter->index0 < iter->gpt.pe_count) {
+ gpt_part = (const struct disk_gpt_part_entry *)
+ (iter->data + iter->index0 * iter->gpt.pe_size);
- if (!(iter = malloc(sizeof(struct part_iter)))) {
- error("Couldn't allocate memory for the iterator.\n");
- goto bail;
+ if (notsane_gpt(iter)) {
+ iter->status = PI_INSANE;
+ return iter->status;
+ }
+
+ if (!guid_is0(&gpt_part->type) || (iter->flags & PIF_STEPALL))
+ break;
}
+ /* no more partitions ? */
+ if (iter->index0 == iter->gpt.pe_count) {
+ iter->status = PI_DONE;
+ return iter->status;
+ }
+ /* gpt_part is guaranteed to be valid here */
+ iter->index = iter->index0 + 1;
+ iter->abs_lba = gpt_part->lba_first;
+ iter->length = gpt_part->lba_last - gpt_part->lba_first + 1;
+ iter->record = (char *)gpt_part;
+ memcpy(&iter->gpt.part_guid, &gpt_part->uid, sizeof(struct guid));
+ gpt_conv_label(iter);
- memset(iter, 0, sizeof(struct part_iter));
- iter->type = type;
+#ifdef DEBUG
+ disk_gpt_part_dump(gpt_part);
+#endif
- if (type->ctor(iter, &ap)) {
- badctor = -1;
- error("Cannot initialize the iterator.\n");
- goto bail;
- }
+ return iter->status;
+}
-bail:
- va_end(ap);
- if (badctor) {
- free(iter);
- iter = NULL;
- }
+static struct part_iter *pi_alloc(void)
+{
+ struct part_iter *iter;
+ if (!(iter = malloc(sizeof *iter)))
+ critm();
+ else
+ memset(iter, 0, sizeof *iter);
return iter;
}
-/**
- * pi_del() - delete iterator
- * @iter: iterator double pointer
- *
- **/
-
+/* pi_del() - delete iterator */
void pi_del(struct part_iter **_iter)
{
- struct part_iter *iter;
-
if(!_iter || !*_iter)
return;
- iter = *_iter;
-
-#ifdef DEBUG
- if (inv_type(iter->type)) {
- error("This is not a valid iterator.\n");
- return;
- }
-#endif
-
- iter->type->dtor(iter);
- free(iter);
+ pi_dtor(*_iter);
+ free(*_iter);
*_iter = NULL;
}
-/**
- * pi_begin() - check disk, validate, and get proper iterator
- * @di: diskinfo struct pointer
- *
- * This function checks the disk for GPT or legacy partition table and allocates
- * an appropriate iterator.
- **/
-struct part_iter *pi_begin(const struct disk_info *di, int stepall)
+/* pi_begin() - validate and and get proper iterator for a disk described by di */
+struct part_iter *pi_begin(const struct disk_info *di, int flags)
{
- int setraw = 0;
- struct part_iter *iter = NULL;
+ int gptprot, ret = -1;
+ struct part_iter *iter;
struct disk_dos_mbr *mbr = NULL;
struct disk_gpt_header *gpth = NULL;
struct disk_gpt_part_entry *gptl = NULL;
+ /* Preallocate iterator */
+ if (!(iter = pi_alloc()))
+ goto bail;
+
/* Read MBR */
if (!(mbr = disk_read_sectors(di, 0, 1))) {
- error("Couldn't read first disk sector.\n");
+ error("Couldn't read the first disk sector.");
goto bail;
}
- setraw = -1;
-
- /* Check for MBR magic*/
+ /* Check for MBR magic */
if (mbr->sig != disk_mbr_sig_magic) {
- error("No MBR magic.\n");
+ warn("No MBR magic, treating disk as raw.");
+ /* looks like RAW */
+ ret = pi_ctor(iter, di, flags);
goto bail;
}
/* Check for GPT protective MBR */
- if (mbr->table[0].ostype == 0xEE) {
+ gptprot = 0;
+ for (size_t i = 0; i < 4; i++)
+ gptprot |= (mbr->table[i].ostype == 0xEE);
+ if (gptprot && !(flags & PIF_PREFMBR)) {
if (!(gpth = disk_read_sectors(di, 1, 1))) {
- error("Couldn't read potential GPT header.\n");
+ error("Couldn't read potential GPT header.");
goto bail;
}
}
if (gpth && gpth->rev.uint32 == 0x00010000 &&
- !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
+ !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) {
/* looks like GPT v1.0 */
uint64_t gpt_loff; /* offset to GPT partition list in sectors */
uint64_t gpt_lsiz; /* size of GPT partition list in bytes */
uint64_t gpt_lcnt; /* size of GPT partition in sectors */
#ifdef DEBUG
- puts("Looks like a GPT v1.0 disk.");
+ dprintf("Looks like a GPT v1.0 disk.\n");
disk_gpt_header_dump(gpth);
#endif
/* Verify checksum, fallback to backup, then bail if invalid */
@@ -753,48 +607,45 @@ struct part_iter *pi_begin(const struct disk_info *di, int stepall)
* it as a sanity check base. EFI doesn't specify max (AFAIK).
* Apart from that, some extensive sanity checks.
*/
- if (!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u ||
+ if (!(flags & PIF_RELAX) && (
+ !gpt_loff || !gpt_lsiz || gpt_lcnt > 255u ||
gpth->lba_first_usable > gpth->lba_last_usable ||
!sane(gpt_loff, gpt_lcnt) ||
gpt_loff + gpt_lcnt > gpth->lba_first_usable ||
!sane(gpth->lba_last_usable, gpt_lcnt) ||
gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt ||
gpth->lba_alt >= di->lbacnt ||
- gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
- error("Invalid GPT header's values.\n");
+ gpth->part_size < sizeof *gptl)) {
+ error("Invalid GPT header's values.");
goto bail;
}
- if (!(gptl = disk_read_sectors(di, gpt_loff, (uint8_t)gpt_lcnt))) {
- error("Couldn't read GPT partition list.\n");
+ if (!(gptl = disk_read_sectors(di, gpt_loff, gpt_lcnt))) {
+ error("Couldn't read GPT partition list.");
goto bail;
}
/* Check array checksum(s). */
- if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
- error("WARNING: GPT partition list checksum invalid, trying backup.\n");
+ if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
+ warn("Checksum of the main GPT partition list is invalid, trying backup.");
free(gptl);
/* secondary array directly precedes secondary header */
- if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, (uint8_t)gpt_lcnt))) {
- error("Couldn't read backup GPT partition list.\n");
+ if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, gpt_lcnt))) {
+ error("Couldn't read backup GPT partition list.");
goto bail;
}
- if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
- error("Backup GPT partition list checksum invalid.\n");
+ if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, gpt_lsiz)) {
+ error("Checksum of the backup GPT partition list is invalid, giving up.");
goto bail;
}
}
- /* allocate iterator and exit */
- iter = pi_new(typegpt, di, stepall, gpth, gptl);
+ /* looks like GPT */
+ ret = pi_gpt_ctor(iter, di, flags, gpth, gptl);
} else {
/* looks like MBR */
- iter = pi_new(typedos, di, stepall, mbr);
+ ret = pi_dos_ctor(iter, di, flags, mbr);
}
-
- setraw = 0;
bail:
- if (setraw) {
- error("WARNING: treating disk as raw.\n");
- iter = pi_new(typeraw, di, stepall);
- }
+ if (ret < 0)
+ free(iter);
free(mbr);
free(gpth);
free(gptl);
diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h
index 7deeb534..13dec848 100644
--- a/com32/chain/partiter.h
+++ b/com32/chain/partiter.h
@@ -1,8 +1,9 @@
/* ----------------------------------------------------------------------- *
*
- * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
- * Copyright 2010 Michal Soltys
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
* Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -33,24 +34,26 @@
* Provides disk / partition iteration.
*/
-#ifndef _COM32_CHAIN_PARTITER_H
-#define _COM32_CHAIN_PARTITER_H
+#ifndef COM32_CHAIN_PARTITER_H
+#define COM32_CHAIN_PARTITER_H
#include <stdint.h>
#include <syslinux/disk.h>
-#define PI_ERRLOAD 3
-#define PI_INSANE 2
-#define PI_DONE 1
-#define PI_OK 0
+/* status */
+
+enum {PI_OK, PI_DONE, PI_INSANE, PI_ERRLOAD};
+
+/* flags */
+
+enum {PIF_STEPALL = 1, PIF_RELAX = 2, PIF_PREFMBR = 4};
struct itertype;
struct part_iter;
struct itertype {
- int (*ctor)(struct part_iter *, va_list *);
void (*dtor)(struct part_iter *);
- struct part_iter *(*next) (struct part_iter *);
+ int (*next)(struct part_iter *);
};
#define PI_GPTLABSIZE ((int)sizeof(((struct disk_gpt_part_entry *)0)->name))
@@ -59,29 +62,29 @@ struct part_iter {
const struct itertype *type;
char *data;
char *record;
- uint64_t start_lba;
+ uint64_t abs_lba;
uint64_t length;
- int index;
- int rawindex;
+ int index0; /* including holes, from -1 (disk, then parts from 0) */
+ int index; /* excluding holes, from 0 (disk, then parts from 1), -1 means hole, if PIF_STEPALL is set */
+ int flags; /* flags, see #defines above */
+ int status; /* current status, see enums above */
struct disk_info di;
- int stepall;
- int status;
- /* internal */
- int index0;
- union _sub {
- struct _dos {
- uint32_t disk_sig;
- uint32_t nebr_lba;
- uint32_t cebr_lba;
- /* internal */
- uint32_t ebr_start;
- uint32_t ebr_size;
- uint32_t bebr_start;
- uint32_t bebr_size;
- int bebr_index0;
- int skipcnt;
+ union {
+ struct {
+ uint32_t disk_sig; /* 32bit disk signature as stored in MBR */
+
+ uint32_t bebr_lba; /* absolute lba of base extended partition */
+ uint32_t bebr_siz; /* size of base extended partition */
+
+ uint32_t cebr_lba; /* absolute lba of curr ext. partition */
+ uint32_t cebr_siz; /* size of curr ext. partition */
+ uint32_t nebr_lba; /* absolute lba of next ext. partition */
+ uint32_t nebr_siz; /* size of next ext. partition */
+
+ int bebr_index0; /* index of (0-3) of base ext. part., -1 if not present in MBR */
+ int logskipcnt; /* how many logical holes were skipped */
} dos;
- struct _gpt {
+ struct {
struct guid disk_guid;
struct guid part_guid;
char part_label[PI_GPTLABSIZE/2+1];
@@ -90,17 +93,31 @@ struct part_iter {
uint64_t ufirst;
uint64_t ulast;
} gpt;
- } sub;
+ };
};
extern const struct itertype * const typedos;
extern const struct itertype * const typegpt;
extern const struct itertype * const typeraw;
-struct part_iter *pi_begin(const struct disk_info *, int stepall);
-struct part_iter *pi_new(const struct itertype *, ...);
+struct part_iter *pi_begin(const struct disk_info *, int flags);
void pi_del(struct part_iter **);
-int pi_next(struct part_iter **);
+
+static inline int pi_errored(struct part_iter *iter)
+{
+ return iter->status > PI_DONE;
+}
+
+/* inline virtuals */
+static inline int pi_next(struct part_iter *iter)
+{
+ return iter->type->next(iter);
+}
+
+static inline void pi_dtor(struct part_iter *iter)
+{
+ iter->type->dtor(iter);
+}
#endif
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
index fb59551b..b17997f7 100644
--- a/com32/chain/utility.c
+++ b/com32/chain/utility.c
@@ -1,10 +1,43 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
#include <com32.h>
+#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <fs.h>
#include <syslinux/disk.h>
+#include <syslinux/pmapi.h>
#include "utility.h"
static const char *bpbtypes[] = {
@@ -16,18 +49,9 @@ static const char *bpbtypes[] = {
[5] = "4.0",
[6] = "8.0 (NT+)",
[7] = "7.0",
+ [8] = "exFAT",
};
-void error(const char *msg)
-{
- fputs(msg, stderr);
-}
-
-int guid_is0(const struct guid *guid)
-{
- return !*(const uint64_t *)guid && !*((const uint64_t *)guid + 1);
-}
-
void wait_key(void)
{
int cnt;
@@ -46,7 +70,29 @@ void wait_key(void)
} while (!cnt || (cnt < 0 && errno == EAGAIN));
}
-void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode)
+int guid_is0(const struct guid *guid)
+{
+ return
+ !(guid->data1 ||
+ guid->data2 ||
+ guid->data3 ||
+ guid->data4);
+}
+
+/*
+ * mode explanation:
+ *
+ * cnul - "strict" mode, never returning higher value than obtained from cbios
+ * cadd - if the disk is larger than reported geometry /and/ if the geometry has
+ * less cylinders than 1024 - it means that the total size is somewhere
+ * between cs and cs+1; in this particular case, we bump the cs to be able
+ * to return matching chs triplet
+ * cmax - assume we can use any cylinder value
+ *
+ * by default cadd seems most reasonable, giving consistent results with e.g.
+ * sfdisk's behavior
+ */
+void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, int mode)
{
uint32_t c, h, s, t;
uint32_t cs, hs, ss;
@@ -59,9 +105,10 @@ void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t m
cs = di->cyl;
hs = di->head;
ss = di->spt;
- if (mode == l2c_cadd && cs < 1024 && di->lbacnt > cs*hs*ss)
- cs++;
- else if (mode == l2c_cmax)
+ if (mode == L2C_CADD) {
+ if (cs < 1024 && di->lbacnt > cs*hs*ss)
+ cs++;
+ } else if (mode == L2C_CMAX)
cs = 1024;
} else {
if (di->disk & 0x80) {
@@ -80,8 +127,8 @@ void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t m
h = hs - 1;
c = cs - 1;
} else {
- s = ((uint32_t)lba % ss) + 1;
- t = (uint32_t)lba / ss;
+ s = (lba % ss) + 1;
+ t = lba / ss;
h = t % hs;
c = t / hs;
}
@@ -93,54 +140,43 @@ void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t m
uint32_t get_file_lba(const char *filename)
{
- com32sys_t inregs;
- uint32_t lba;
+ struct com32_filedata fd;
+ uint32_t lba = 0;
+ int size = 65536;
+ char *buf;
- /* Start with clean registers */
- memset(&inregs, 0, sizeof(com32sys_t));
+ buf = lmalloc(size);
+ if (!buf)
+ return 0;
/* Put the filename in the bounce buffer */
- strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
-
- /* Call comapi_open() which returns a structure pointer in SI
- * to a structure whose first member happens to be the LBA.
- */
- inregs.eax.w[0] = 0x0006;
- inregs.esi.w[0] = OFFS(__com32.cs_bounce);
- inregs.es = SEG(__com32.cs_bounce);
- __com32.cs_intcall(0x22, &inregs, &inregs);
+ strlcpy(buf, filename, size);
- if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
- return 0; /* Filename not found */
+ if (open_file(buf, O_RDONLY, &fd) <= 0) {
+ goto fail; /* Filename not found */
}
/* Since the first member is the LBA, we simply cast */
- lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
-
- /* Clean the registers for the next call */
- memset(&inregs, 0, sizeof(com32sys_t));
-
- /* Put the filename in the bounce buffer */
- strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
+ lba = *((uint32_t *) MK_PTR(0, fd.handle));
/* Call comapi_close() to free the structure */
- inregs.eax.w[0] = 0x0008;
- inregs.esi.w[0] = OFFS(__com32.cs_bounce);
- inregs.es = SEG(__com32.cs_bounce);
- __com32.cs_intcall(0x22, &inregs, &inregs);
+ close_file(fd.handle);
+fail:
+ lfree(buf);
return lba;
}
/* drive offset detection */
-int drvoff_detect(int type, unsigned int *off)
+int drvoff_detect(int type)
{
if (bpbV40 <= type && type <= bpbVNT) {
- *off = 0x24;
+ return 0x24;
} else if (type == bpbV70) {
- *off = 0x40;
- } else
- return 0;
+ return 0x40;
+ } else if (type == bpbEXF) {
+ return 0x6F;
+ }
return -1;
}
@@ -152,6 +188,12 @@ int bpb_detect(const uint8_t *sec, const char *tag)
{
int a, b, c, jmp = -1, rev = 0;
+ /* exFAT mess first (media descriptor is 0 here) */
+ if (!memcmp(sec + 0x03, "EXFAT ", 8)) {
+ rev = bpbEXF;
+ goto out;
+ }
+
/* media descriptor check */
if ((sec[0x15] & 0xF0) != 0xF0)
goto out;
diff --git a/com32/chain/utility.h b/com32/chain/utility.h
index 8a08be71..596017bb 100644
--- a/com32/chain/utility.h
+++ b/com32/chain/utility.h
@@ -1,29 +1,74 @@
-#ifndef _COM32_CHAIN_UTILITY_H
-#define _COM32_CHAIN_UTILITY_H
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010-2012 Michal Soltys
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_UTILITY_H
+#define COM32_CHAIN_UTILITY_H
#include <stdint.h>
+#include <stdio.h>
#include <syslinux/disk.h>
+#include <syslinux/movebits.h>
-#define bpbUNK 0
-#define bpbV20 1
-#define bpbV30 2
-#define bpbV32 3
-#define bpbV34 4
-#define bpbV40 5
-#define bpbVNT 6
-#define bpbV70 7
+/* most (all ?) bpb "types" known to humankind as of 2012 */
+enum {bpbUNK, bpbV20, bpbV30, bpbV32, bpbV34, bpbV40, bpbVNT, bpbV70, bpbEXF};
-#define l2c_cnul 0
-#define l2c_cadd 1
-#define l2c_cmax 2
+/* see utility.c for details */
+enum {L2C_CNUL, L2C_CADD, L2C_CMAX};
+
+/* first usable and first unusable offsets */
+#define dosmin ((addr_t)0x500u)
+#define dosmax ((addr_t)(*(uint16_t *) 0x413 << 10))
-void error(const char *msg);
-int guid_is0(const struct guid *guid);
void wait_key(void);
-void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode);
+void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, int mode);
uint32_t get_file_lba(const char *filename);
-int drvoff_detect(int type, unsigned int *off);
+int drvoff_detect(int type);
int bpb_detect(const uint8_t *bpb, const char *tag);
+int guid_is0(const struct guid *guid);
+
+static inline int warn(const char *x)
+{
+ return fprintf(stderr, "WARN: %s\n", x);
+}
+
+static inline int error(const char *x)
+{
+ return fprintf(stderr, "ERR: %s\n", x);
+}
+
+static inline int crit(const char *x)
+{
+ return fprintf(stderr, "CRIT: %s @%s:%d\n", x, __FILE__, __LINE__);
+}
+
+#define critm() crit("Malloc failure.")
#endif
diff --git a/com32/cmenu/Makefile b/com32/cmenu/Makefile
index 446bbcdd..d51b2e84 100644
--- a/com32/cmenu/Makefile
+++ b/com32/cmenu/Makefile
@@ -17,41 +17,42 @@
NOGPL := 1
-# This must be defined before com32.mk is included
-LIBS = libmenu/libmenu.a
+LIBS = libmenu/libmenu.c32 \
+ $(com32)/libutil/libutil.c32 \
+ $(com32)/lib/libcom32.c32
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
CFLAGS += -I./libmenu
LIBMENU = libmenu/syslnx.o libmenu/com32io.o libmenu/tui.o \
- libmenu/menu.o libmenu/passwords.o libmenu/des.o libmenu/help.o
+ libmenu/menu.o libmenu/passwords.o libmenu/des.o libmenu/help.o \
+ $(com32)/libutil/libutil.c32 $(com32)/lib/libcom32.c32
CMENUS = $(patsubst %.c,%.c32,$(wildcard *.c))
IMENUS = $(patsubst %.menu,%.c32,$(wildcard *.menu))
-MENUS = $(CMENUS) $(IMENUS)
+MENUS = $(LIBS) $(CMENUS) $(IMENUS)
.SUFFIXES: .S .c .o .elf .c32 .menu
.PRECIOUS: %.c
%.c: %.menu adv_menu.tpl
- python menugen.py --input=$< --output=$@ --template=adv_menu.tpl
+ $(PYTHON) menugen.py --input=$< --output=$@ --template=adv_menu.tpl
-all: menus
+all: menus
-libmenu/libmenu.a: $(LIBMENU)
- -rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
+libmenu/libmenu.elf: $(LIBMENU)
+ $(LD) -shared $(LDFLAGS) -soname $(patsubst %.elf,%.c32,$(@F)) \
+ -o $@ $^
tidy dist:
- rm -f *.o *.lo *.a *.lst *.elf .*.d */.*.d
+ rm -f *.o *.lo *.lst *.elf */*.o */*.elf .*.d */.*.d
libclean:
- rm -f libmenu/*.o libmenu/*.a
+ rm -f libmenu/*.c32
clean: tidy menuclean libclean
rm -f *.lss *.c32 *.com
diff --git a/com32/cmenu/adv_menu.tpl b/com32/cmenu/adv_menu.tpl
index e60383f8..89317095 100644
--- a/com32/cmenu/adv_menu.tpl
+++ b/com32/cmenu/adv_menu.tpl
@@ -34,7 +34,7 @@ modify this template to suit your needs
#define NULL ((void *) 0)
#endif
-#include "menu.h"
+#include "cmenu.h"
#include "help.h"
#include "passwords.h"
#include "com32io.h"
diff --git a/com32/cmenu/complex.c b/com32/cmenu/complex.c
index b80005d1..d6658fb3 100644
--- a/com32/cmenu/complex.c
+++ b/com32/cmenu/complex.c
@@ -14,7 +14,7 @@
#define NULL ((void *) 0)
#endif
-#include "menu.h"
+#include "cmenu.h"
#include "com32io.h"
#include "help.h"
#include "passwords.h"
diff --git a/com32/cmenu/display.c b/com32/cmenu/display.c
index 3acdf6aa..59612f9f 100644
--- a/com32/cmenu/display.c
+++ b/com32/cmenu/display.c
@@ -16,7 +16,7 @@
#include "help.h"
#include "com32io.h"
-#include "menu.h"
+#include "cmenu.h"
#include "tui.h"
#include <stdlib.h>
#include <com32.h>
diff --git a/com32/cmenu/libmenu/menu.h b/com32/cmenu/libmenu/cmenu.h
index 141d2ef0..141d2ef0 100644
--- a/com32/cmenu/libmenu/menu.h
+++ b/com32/cmenu/libmenu/cmenu.h
diff --git a/com32/cmenu/libmenu/help.h b/com32/cmenu/libmenu/help.h
index de01b46e..383cc3c5 100644
--- a/com32/cmenu/libmenu/help.h
+++ b/com32/cmenu/libmenu/help.h
@@ -13,7 +13,7 @@
#ifndef __HELP_H_
#define __HELP_H_
-#include "menu.h"
+#include "cmenu.h"
#include "com32io.h"
#include "tui.h"
#include <string.h>
diff --git a/com32/cmenu/libmenu/menu.c b/com32/cmenu/libmenu/menu.c
index 19a7e598..9b1e7ad0 100644
--- a/com32/cmenu/libmenu/menu.c
+++ b/com32/cmenu/libmenu/menu.c
@@ -10,7 +10,7 @@
*
* ----------------------------------------------------------------------- */
-#include "menu.h"
+#include "cmenu.h"
#include "com32io.h"
#include <stdlib.h>
#include <console.h>
diff --git a/com32/cmenu/libmenu/syslnx.c b/com32/cmenu/libmenu/syslnx.c
index 53e2401b..5060c5db 100644
--- a/com32/cmenu/libmenu/syslnx.c
+++ b/com32/cmenu/libmenu/syslnx.c
@@ -12,84 +12,53 @@
#include <string.h>
#include <com32.h>
+#include <core.h>
+#include <graphics.h>
#include "syslnx.h"
+#include <syslinux/config.h>
com32sys_t inreg, outreg; // Global registers for this module
-char issyslinux(void)
-{
- REG_EAX(inreg) = 0x00003000;
- REG_EBX(inreg) = REG_ECX(inreg) = REG_EDX(inreg) = 0xFFFFFFFF;
- __intcall(0x21, &inreg, &outreg);
- return (REG_EAX(outreg) == 0x59530000) &&
- (REG_EBX(outreg) == 0x4c530000) &&
- (REG_ECX(outreg) == 0x4e490000) && (REG_EDX(outreg) == 0x58550000);
-}
-
void runsyslinuxcmd(const char *cmd)
{
- strcpy(__com32.cs_bounce, cmd);
- REG_AX(inreg) = 0x0003; // Run command
- REG_BX(inreg) = OFFS(__com32.cs_bounce);
- REG_ES(inreg) = SEG(__com32.cs_bounce);
- __intcall(0x22, &inreg, &outreg);
+ char *bounce;
+
+ bounce = lmalloc(strlen(cmd) + 1);
+ if (!bounce)
+ return;
+
+ strcpy(bounce, cmd);
+ load_kernel(bounce);
}
void gototxtmode(void)
{
- REG_AX(inreg) = 0x0005;
- __intcall(0x22, &inreg, &outreg);
+ syslinux_force_text_mode();
}
void syslinux_idle(void)
{
- REG_AX(inreg) = 0x0013;
- __intcall(0x22, &inreg, &outreg);
+ __idle();
}
unsigned int getversion(char *deriv, unsigned int *numfun)
{
- REG_AX(inreg) = 0x0001;
- __intcall(0x22, &inreg, &outreg);
if (deriv)
- *deriv = REG_DL(outreg);
+ *deriv = __syslinux_version.filesystem;
if (numfun)
- *numfun = REG_AX(outreg);
- return REG_CX(outreg);
+ *numfun = __syslinux_version.max_api;
+ return __syslinux_version.version;
}
-void runsyslinuximage(const char *cmd, long ipappend)
+char issyslinux(void)
{
- unsigned int numfun = 0;
- char *ptr, *cmdline;
+ return !!getversion(NULL, NULL);
+}
+void runsyslinuximage(const char *cmd, long ipappend)
+{
(void)ipappend; // XXX: Unused?!
- getversion(NULL, &numfun);
- // Function 16h not supported Fall back to runcommand
- if (numfun < 0x16)
- runsyslinuxcmd(cmd);
- // Try the Run Kernel Image function
- // Split command line into
- strcpy(__com32.cs_bounce, cmd);
- ptr = __com32.cs_bounce;
- // serach for first space or end of string
- while ((*ptr) && (*ptr != ' '))
- ptr++;
- if (!*ptr)
- cmdline = ptr; // no command line
- else {
- *ptr++ = '\0'; // terminate kernal name
- cmdline = ptr + 1;
- while (*cmdline != ' ')
- cmdline++; // find first non-space
- }
- // Now call the interrupt
- REG_BX(inreg) = OFFS(cmdline);
- REG_ES(inreg) = SEG(cmdline);
- REG_SI(inreg) = OFFS(__com32.cs_bounce);
- REG_DS(inreg) = SEG(__com32.cs_bounce);
- REG_EDX(inreg) = 0;
-
- __intcall(0x22, &inreg, &outreg); // If successful does not return
+ getversion(NULL, NULL);
+ runsyslinuxcmd(cmd);
}
diff --git a/com32/cmenu/simple.c b/com32/cmenu/simple.c
index 4f602a9a..ba9669fc 100644
--- a/com32/cmenu/simple.c
+++ b/com32/cmenu/simple.c
@@ -14,7 +14,7 @@
#define NULL ((void *) 0)
#endif
-#include "menu.h"
+#include "cmenu.h"
#include "com32io.h"
#include <string.h>
diff --git a/com32/elflink/Makefile b/com32/elflink/Makefile
new file mode 100644
index 00000000..fce1be8c
--- /dev/null
+++ b/com32/elflink/Makefile
@@ -0,0 +1,36 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+MODULES =
+TESTFILES =
+
+all: $(MODULES) $(TESTFILES)
+
+test_memalign.elf : test_memalign.o $(LIBS) $(C_LIBS)
+ $(LD) $(LDFLAGS) -o $@ $^
+
+test_com32.elf: CFLAGS += -DELF_DEBUG
+test_com32.elf: test_com32.o ../lib/libcom32min.a $(LIBGCC)
+ $(LD) -n $(LDFLAGS) -o $@ test_com32.o $(LIBGCC) --whole-archive ../lib/libcom32min.a -Map test_com32.map
+
+tidy dist:
+ rm -f *.o *.lo *.a *.lst *.elf .*.d *.map
+
+clean: tidy
+ rm -f *.lss *.c32 *.lnx *.com
+
+spotless: clean
+ rm -f *~ \#*
+
+install:
+
+-include .*.d
diff --git a/com32/elflink/ldlinux/Makefile b/com32/elflink/ldlinux/Makefile
new file mode 100644
index 00000000..556f93a5
--- /dev/null
+++ b/com32/elflink/ldlinux/Makefile
@@ -0,0 +1,50 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2011-2013 Intel Corporation - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+topdir = ../../..
+MAKEDIR = $(topdir)/mk
+include $(MAKEDIR)/elf.mk
+
+CFLAGS += -I$(topdir)/core/elflink -I$(topdir)/core/include -I$(topdir)/com32/lib -fvisibility=hidden
+LIBS = --whole-archive $(com32)/lib/libcom32min.a
+
+BTARGET = ldlinux.c32
+
+all: $(BTARGET) ldlinux_lnx.a
+
+ldlinux.elf : ldlinux.o cli.o readconfig.o refstr.o colors.o getadv.o \
+ adv.o execute.o chainboot.o kernel.o get_key.o \
+ advwrite.o setadv.o loadhigh.o msg.o
+ $(LD) $(LDFLAGS) -soname $(patsubst %.elf,%.c32,$(@F)) -o $@ $^ $(LIBS)
+
+LNXCFLAGS += -D__export='__attribute__((visibility("default")))'
+LNXLIBOBJS = get_key.lo
+ldlinux_lnx.a: $(LNXLIBOBJS)
+ rm -f $@
+ $(AR) cq $@ $(LNXLIBOBJS)
+ $(RANLIB) $@
+
+tidy dist:
+ rm -f *.o *.lo *.a *.lst .*.d
+
+clean: tidy
+ rm -f *.lss *.lnx *.com
+
+spotless: clean
+ rm -f *~ \#* $(BTARGET)
+
+install: all
+ mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR)
+ install -m 644 $(BTARGET) $(INSTALLROOT)$(AUXDIR)
+
+
+-include .*.d
diff --git a/com32/lib/syslinux/adv.c b/com32/elflink/ldlinux/adv.c
index be38e89d..4c3ad508 100644
--- a/com32/lib/syslinux/adv.c
+++ b/com32/elflink/ldlinux/adv.c
@@ -33,15 +33,21 @@
#include <syslinux/adv.h>
#include <klibc/compiler.h>
+#include <inttypes.h>
#include <com32.h>
-void *__syslinux_adv_ptr;
-size_t __syslinux_adv_size;
+__export void *__syslinux_adv_ptr;
+__export size_t __syslinux_adv_size;
-void __constructor __syslinux_get_adv(void)
+extern void adv_init(void);
+void __constructor __syslinux_init(void)
{
static com32sys_t reg;
+ /* Initialize the ADV structure */
+ reg.eax.w[0] = 0x0025;
+ __intcall(0x22, &reg, NULL);
+
reg.eax.w[0] = 0x001c;
__intcall(0x22, &reg, &reg);
__syslinux_adv_ptr = MK_PTR(reg.es, reg.ebx.w[0]);
diff --git a/com32/lib/syslinux/advwrite.c b/com32/elflink/ldlinux/advwrite.c
index 4152eea5..35829c1c 100644
--- a/com32/lib/syslinux/advwrite.c
+++ b/com32/elflink/ldlinux/advwrite.c
@@ -35,7 +35,7 @@
#include <klibc/compiler.h>
#include <com32.h>
-int syslinux_adv_write(void)
+__export int syslinux_adv_write(void)
{
static com32sys_t reg;
diff --git a/com32/elflink/ldlinux/chainboot.c b/com32/elflink/ldlinux/chainboot.c
new file mode 100644
index 00000000..27d4618c
--- /dev/null
+++ b/com32/elflink/ldlinux/chainboot.c
@@ -0,0 +1,156 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2012 Intel Corporation, author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * chainbooting - replace the current bootloader completely. This
+ * is BIOS-specific.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dprintf.h>
+
+#include <com32.h>
+#include <sys/exec.h>
+#include <sys/io.h>
+#include "core.h"
+#include "menu.h"
+#include "fs.h"
+#include "config.h"
+#include "localboot.h"
+#include "bios.h"
+
+#include <syslinux/boot.h>
+#include <syslinux/bootrm.h>
+#include <syslinux/movebits.h>
+#include <syslinux/config.h>
+
+void chainboot_file(const char *file, uint32_t type)
+{
+ uint8_t keeppxe = 0;
+ const union syslinux_derivative_info *sdi;
+ struct syslinux_rm_regs regs;
+ struct syslinux_movelist *fraglist = NULL;
+ struct syslinux_memmap *mmap = NULL;
+ struct com32_filedata fd;
+ com32sys_t reg;
+ char *stack;
+ void *buf;
+ int rv, max, size;
+
+ max = 0xA0000; /* Maximum load */
+ buf = malloc(max);
+ if (!buf)
+ goto bail;
+
+ rv = open_file(file, O_RDONLY, &fd);
+ if (rv == -1)
+ goto bail;
+
+ reg.eax.l = max;
+ reg.ebx.l = 0;
+ reg.edx.w[0] = 0;
+ reg.edi.l = (uint32_t)buf;
+ reg.ebp.l = -1; /* XXX: limit? */
+ reg.esi.w[0] = rv;
+
+ pm_load_high(&reg);
+
+ size = reg.edi.l - (unsigned long)buf;
+ if (size > 0xA0000 - 0x7C00) {
+ printf("Too large for a boostrap (need LINUX instead of KERNEL?)\n");
+ goto bail;
+ }
+
+ mmap = syslinux_memory_map();
+ if (!mmap)
+ goto bail;
+
+ sdi = syslinux_derivative_info();
+
+ memset(&regs, 0, sizeof(regs));
+ regs.ip = 0x7c00;
+
+ if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX ||
+ sdi->c.filesystem == SYSLINUX_FS_EXTLINUX) {
+ if (syslinux_add_movelist(&fraglist, 0x800 - 18,
+ (addr_t)sdi->r.esbx, 16))
+ goto bail;
+
+ /* DS:SI points to partition info */
+ regs.esi.l = 0x800 - 18;
+ }
+
+ /*
+ * For a BSS boot sector we have to transfer the
+ * superblock.
+ */
+ if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX &&
+ type == IMAGE_TYPE_BSS && this_fs->fs_ops->copy_super(buf))
+ goto bail;
+
+ if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
+ keeppxe = 0x03; /* Chainloading + keep PXE */
+ stack = (char *)sdi->r.fssi;
+
+ /*
+ * Set up the registers with their initial values
+ */
+
+ regs.eax.l = *(uint32_t *)&stack[36];
+ regs.ecx.l = *(uint32_t *)&stack[32];
+ regs.edx.l = *(uint32_t *)&stack[28];
+ regs.ebx.l = *(uint32_t *)&stack[24];
+ regs.esp.l = sdi->rr.r.esi.w[0] + 44;
+ regs.ebp.l = *(uint32_t *)&stack[16];
+ regs.esi.l = *(uint32_t *)&stack[12];
+ regs.edi.l = *(uint32_t *)&stack[8];
+ regs.es = *(uint16_t *)&stack[4];
+ regs.ss = sdi->rr.r.fs;
+ regs.ds = *(uint16_t *)&stack[6];
+ regs.fs = *(uint16_t *)&stack[2];
+ regs.gs = *(uint16_t *)&stack[0];
+ } else {
+ const uint16_t *esdi = (const uint16_t *)sdi->disk.esdi_ptr;
+
+ regs.esp.l = (uint16_t)(unsigned long)StackBuf + 44;
+
+ /*
+ * DON'T DO THIS FOR PXELINUX...
+ * For PXE, ES:BX -> PXENV+, and this would
+ * corrupt that use.
+ *
+ * Restore ES:DI -> $PnP (if we were ourselves
+ * called that way...)
+ */
+ regs.edi.w[0] = esdi[0]; /* New DI */
+ regs.es = esdi[2]; /* New ES */
+
+ regs.edx.l = sdi->rr.r.edx.b[0]; /* Drive number -> DL */
+ }
+
+ if (syslinux_add_movelist(&fraglist, 0x7c00, (addr_t)buf, size))
+ goto bail;
+
+ syslinux_shuffle_boot_rm(fraglist, mmap, keeppxe, &regs);
+
+bail:
+ if (fraglist)
+ syslinux_free_movelist(fraglist);
+ if (mmap)
+ syslinux_free_memmap(mmap);
+ if (buf)
+ free(buf);
+ return;
+}
diff --git a/com32/elflink/ldlinux/cli.c b/com32/elflink/ldlinux/cli.c
new file mode 100644
index 00000000..6ff30c64
--- /dev/null
+++ b/com32/elflink/ldlinux/cli.c
@@ -0,0 +1,486 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <console.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <syslinux/config.h>
+#include <setjmp.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <minmax.h>
+#include <linux/list.h>
+#include <sys/exec.h>
+#include <sys/module.h>
+#include <dprintf.h>
+#include <core.h>
+
+#include "getkey.h"
+#include "menu.h"
+#include "cli.h"
+#include "config.h"
+
+static struct list_head cli_history_head;
+
+void clear_screen(void)
+{
+ //dprintf("enter");
+ fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout);
+}
+
+static int mygetkey_timeout(clock_t *kbd_to, clock_t *tto)
+{
+ clock_t t0, t1;
+ int key;
+
+ t0 = times(NULL);
+ key = get_key(stdin, *kbd_to ? *kbd_to : *tto);
+
+ /* kbdtimeout only applies to the first character */
+ if (*kbd_to)
+ *kbd_to = 0;
+
+ t1 = times(NULL) - t0;
+ if (*tto) {
+ /* Timed out. */
+ if (*tto <= (long long)t1)
+ key = KEY_NONE;
+ else {
+ /* Did it wrap? */
+ if (*tto > totaltimeout)
+ key = KEY_NONE;
+
+ *tto -= t1;
+ }
+ }
+
+ return key;
+}
+
+static const char * cmd_reverse_search(int *cursor, clock_t *kbd_to,
+ clock_t *tto)
+{
+ int key;
+ int i = 0;
+ char buf[MAX_CMDLINE_LEN];
+ const char *p = NULL;
+ struct cli_command *last_found;
+ struct cli_command *last_good = NULL;
+
+ last_found = list_entry(cli_history_head.next, typeof(*last_found), list);
+
+ memset(buf, 0, MAX_CMDLINE_LEN);
+
+ printf("\033[1G\033[1;36m(reverse-i-search)`': \033[0m");
+ while (1) {
+ key = mygetkey_timeout(kbd_to, tto);
+
+ if (key == KEY_CTRL('C')) {
+ return NULL;
+ } else if (key == KEY_CTRL('R')) {
+ if (i == 0)
+ continue; /* User typed nothing yet */
+ /* User typed 'CTRL-R' again, so try the next */
+ last_found = list_entry(last_found->list.next, typeof(*last_found), list);
+ } else if (key >= ' ' && key <= 'z') {
+ buf[i++] = key;
+ } else {
+ /* Treat other input chars as terminal */
+ break;
+ }
+
+ while (last_found) {
+ p = strstr(last_found->command, buf);
+ if (p)
+ break;
+
+ if (list_is_last(&last_found->list, &cli_history_head))
+ break;
+
+ last_found = list_entry(last_found->list.next, typeof(*last_found), list);
+ }
+
+ if (!p && !last_good) {
+ return NULL;
+ } else if (!p) {
+ continue;
+ } else {
+ last_good = last_found;
+ *cursor = p - last_good->command;
+ }
+
+ printf("\033[?7l\033[?25l");
+ /* Didn't handle the line wrap case here */
+ printf("\033[1G\033[1;36m(reverse-i-search)\033[0m`%s': %s",
+ buf, last_good->command ? : "");
+ printf("\033[K\r");
+ }
+
+ return last_good ? last_good->command : NULL;
+}
+
+
+
+const char *edit_cmdline(const char *input, int top /*, int width */ ,
+ int (*pDraw_Menu) (int, int, int),
+ void (*show_fkey) (int), bool *timedout)
+{
+ char cmdline[MAX_CMDLINE_LEN] = { };
+ int key, len, prev_len, cursor;
+ int redraw = 0;
+ int x, y;
+ bool done = false;
+ const char *ret;
+ int width = 0;
+ struct cli_command *comm_counter = NULL;
+ clock_t kbd_to = kbdtimeout;
+ clock_t tto = totaltimeout;
+
+ if (!width) {
+ int height;
+ if (getscreensize(1, &height, &width))
+ width = 80;
+ }
+
+ len = cursor = 0;
+ prev_len = 0;
+ x = y = 0;
+
+ /*
+ * Before we start messing with the x,y coordinates print 'input'
+ * so that it follows whatever text has been written to the screen
+ * previously.
+ */
+ printf("%s ", input);
+
+ while (!done) {
+ if (redraw > 1) {
+ /* Clear and redraw whole screen */
+ /* Enable ASCII on G0 and DEC VT on G1; do it in this order
+ to avoid confusing the Linux console */
+ clear_screen();
+ if (pDraw_Menu)
+ (*pDraw_Menu) (-1, top, 1);
+ prev_len = 0;
+ printf("\033[2J\033[H");
+ // printf("\033[0m\033[2J\033[H");
+ }
+
+ if (redraw > 0) {
+ int dy, at;
+
+ prev_len = max(len, prev_len);
+
+ /* Redraw the command line */
+ printf("\033[?25l");
+ printf("\033[1G%s ", input);
+
+ x = strlen(input);
+ y = 0;
+ at = 0;
+ while (at < prev_len) {
+ putchar(at >= len ? ' ' : cmdline[at]);
+ at++;
+ x++;
+ if (x >= width) {
+ printf("\r\n");
+ x = 0;
+ y++;
+ }
+ }
+ printf("\033[K\r");
+
+ dy = y - (cursor + strlen(input) + 1) / width;
+ x = (cursor + strlen(input) + 1) % width;
+
+ if (dy) {
+ printf("\033[%dA", dy);
+ y -= dy;
+ }
+ if (x)
+ printf("\033[%dC", x);
+ printf("\033[?25h");
+ prev_len = len;
+ redraw = 0;
+ }
+
+ key = mygetkey_timeout(&kbd_to, &tto);
+
+ switch (key) {
+ case KEY_NONE:
+ /* We timed out. */
+ *timedout = true;
+ return NULL;
+
+ case KEY_CTRL('L'):
+ redraw = 2;
+ break;
+
+ case KEY_ENTER:
+ case KEY_CTRL('J'):
+ ret = cmdline;
+ done = true;
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_DEL:
+ if (cursor) {
+ memmove(cmdline + cursor - 1, cmdline + cursor,
+ len - cursor + 1);
+ len--;
+ cursor--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('D'):
+ case KEY_DELETE:
+ if (cursor < len) {
+ memmove(cmdline + cursor, cmdline + cursor + 1, len - cursor);
+ len--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('U'):
+ if (len) {
+ len = cursor = 0;
+ cmdline[len] = '\0';
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('W'):
+ if (cursor) {
+ int prevcursor = cursor;
+
+ while (cursor && my_isspace(cmdline[cursor - 1]))
+ cursor--;
+
+ while (cursor && !my_isspace(cmdline[cursor - 1]))
+ cursor--;
+
+#if 0
+ memmove(cmdline + cursor, cmdline + prevcursor,
+ len - prevcursor + 1);
+#else
+ {
+ int i;
+ char *q = cmdline + cursor;
+ char *p = cmdline + prevcursor;
+ for (i = 0; i < len - prevcursor + 1; i++)
+ *q++ = *p++;
+ }
+#endif
+ len -= (prevcursor - cursor);
+ redraw = 1;
+ }
+ break;
+
+ case KEY_LEFT:
+ case KEY_CTRL('B'):
+ if (cursor) {
+ cursor--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_RIGHT:
+ case KEY_CTRL('F'):
+ if (cursor < len) {
+ putchar(cmdline[cursor]);
+ cursor++;
+ x++;
+ if (x >= width) {
+ printf("\r\n");
+ y++;
+ x = 0;
+ }
+ }
+ break;
+
+ case KEY_CTRL('K'):
+ if (cursor < len) {
+ cmdline[len = cursor] = '\0';
+ redraw = 1;
+ }
+ break;
+
+ case KEY_HOME:
+ case KEY_CTRL('A'):
+ if (cursor) {
+ cursor = 0;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_END:
+ case KEY_CTRL('E'):
+ if (cursor != len) {
+ cursor = len;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ if (show_fkey != NULL) {
+ (*show_fkey) (key);
+ redraw = 1;
+ }
+ break;
+ case KEY_CTRL('P'):
+ case KEY_UP:
+ {
+ if (!list_empty(&cli_history_head)) {
+ struct list_head *next;
+
+ if (!comm_counter)
+ next = cli_history_head.next;
+ else
+ next = comm_counter->list.next;
+
+ comm_counter =
+ list_entry(next, typeof(*comm_counter), list);
+
+ if (&comm_counter->list != &cli_history_head)
+ strcpy(cmdline, comm_counter->command);
+
+ cursor = len = strlen(cmdline);
+ redraw = 1;
+ }
+ }
+ break;
+ case KEY_CTRL('N'):
+ case KEY_DOWN:
+ {
+ if (!list_empty(&cli_history_head)) {
+ struct list_head *prev;
+
+ if (!comm_counter)
+ prev = cli_history_head.prev;
+ else
+ prev = comm_counter->list.prev;
+
+ comm_counter =
+ list_entry(prev, typeof(*comm_counter), list);
+
+ if (&comm_counter->list != &cli_history_head)
+ strcpy(cmdline, comm_counter->command);
+
+ cursor = len = strlen(cmdline);
+ redraw = 1;
+ }
+ }
+ break;
+ case KEY_CTRL('R'):
+ {
+ /*
+ * Handle this case in another function, since it's
+ * a kind of special.
+ */
+ const char *p = cmd_reverse_search(&cursor, &kbd_to, &tto);
+ if (p) {
+ strcpy(cmdline, p);
+ len = strlen(cmdline);
+ } else {
+ cmdline[0] = '\0';
+ cursor = len = 0;
+ }
+ redraw = 1;
+ }
+ break;
+ case KEY_TAB:
+ {
+ const char *p;
+ size_t len;
+
+ /* Label completion enabled? */
+ if (nocomplete)
+ break;
+
+ p = cmdline;
+ len = 0;
+ while(*p && !my_isspace(*p)) {
+ p++;
+ len++;
+ }
+
+ print_labels(cmdline, len);
+ redraw = 1;
+ break;
+ }
+ case KEY_CTRL('V'):
+ if (BIOSName)
+ printf("%s%s%s", syslinux_banner,
+ (char *)MK_PTR(0, BIOSName), copyright_str);
+ else
+ printf("%s%s", syslinux_banner, copyright_str);
+
+ redraw = 1;
+ break;
+
+ default:
+ if (key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN - 1) {
+ if (cursor == len) {
+ cmdline[len++] = key;
+ cmdline[len] = '\0';
+ putchar(key);
+ cursor++;
+ x++;
+ if (x >= width) {
+ printf("\r\n\033[K");
+ y++;
+ x = 0;
+ }
+ prev_len++;
+ } else {
+ if (cursor > len)
+ return NULL;
+
+ memmove(cmdline + cursor + 1, cmdline + cursor,
+ len - cursor + 1);
+ cmdline[cursor++] = key;
+ len++;
+ redraw = 1;
+ }
+ }
+ break;
+ }
+ }
+
+ printf("\033[?7h");
+
+ /* Add the command to the history if its length is larger than 0 */
+ len = strlen(ret);
+ if (len > 0) {
+ comm_counter = malloc(sizeof(struct cli_command));
+ comm_counter->command = malloc(sizeof(char) * (len + 1));
+ strcpy(comm_counter->command, ret);
+ list_add(&(comm_counter->list), &cli_history_head);
+ }
+
+ return len ? ret : NULL;
+}
+
+static int __constructor cli_init(void)
+{
+ INIT_LIST_HEAD(&cli_history_head);
+
+ return 0;
+}
+
+static void __destructor cli_exit(void)
+{
+ /* Nothing to do */
+}
diff --git a/com32/elflink/ldlinux/colors.c b/com32/elflink/ldlinux/colors.c
new file mode 100644
index 00000000..68732bdb
--- /dev/null
+++ b/com32/elflink/ldlinux/colors.c
@@ -0,0 +1,184 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <colortbl.h>
+#include "menu.h"
+
+/*
+ * The color/attribute indexes (\1#X, \2#XX, \3#XXX) are as follows
+ *
+ * 00 - screen Rest of the screen
+ * 01 - border Border area
+ * 02 - title Title bar
+ * 03 - unsel Unselected menu item
+ * 04 - hotkey Unselected hotkey
+ * 05 - sel Selection bar
+ * 06 - hotsel Selected hotkey
+ * 07 - scrollbar Scroll bar
+ * 08 - tabmsg Press [Tab] message
+ * 09 - cmdmark Command line marker
+ * 10 - cmdline Command line
+ * 11 - pwdborder Password box border
+ * 12 - pwdheader Password box header
+ * 13 - pwdentry Password box contents
+ * 14 - timeout_msg Timeout message
+ * 15 - timeout Timeout counter
+ * 16 - help Current entry help text
+ * 17 - disabled Disabled menu item
+ */
+
+static const struct color_table default_colors[] = {
+ {"screen", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL},
+ {"border", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL},
+ {"title", "1;36;44", 0xc00090f0, 0x00000000, SHADOW_NORMAL},
+ {"unsel", "37;44", 0x90ffffff, 0x00000000, SHADOW_NORMAL},
+ {"hotkey", "1;37;44", 0xffffffff, 0x00000000, SHADOW_NORMAL},
+ {"sel", "7;37;40", 0xe0000000, 0x20ff8000, SHADOW_ALL},
+ {"hotsel", "1;7;37;40", 0xe0400000, 0x20ff8000, SHADOW_ALL},
+ {"scrollbar", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL},
+ {"tabmsg", "31;40", 0x90ffff00, 0x00000000, SHADOW_NORMAL},
+ {"cmdmark", "1;36;40", 0xc000ffff, 0x00000000, SHADOW_NORMAL},
+ {"cmdline", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL},
+ {"pwdborder", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL},
+ {"pwdheader", "31;47", 0x80ff8080, 0x20ffffff, SHADOW_NORMAL},
+ {"pwdentry", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL},
+ {"timeout_msg", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL},
+ {"timeout", "1;37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL},
+ {"help", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL},
+ {"disabled", "1;30;44", 0x60cccccc, 0x00000000, SHADOW_NORMAL},
+};
+
+#define NCOLORS (sizeof default_colors/sizeof default_colors[0])
+const int message_base_color = NCOLORS;
+const int menu_color_table_size = NCOLORS + 256;
+
+/* Algorithmically generate the msgXX colors */
+void set_msg_colors_global(struct color_table *tbl,
+ unsigned int fg, unsigned int bg,
+ enum color_table_shadow shadow)
+{
+ struct color_table *cp = tbl + message_base_color;
+ unsigned int i;
+ unsigned int fga, bga;
+ unsigned int fgh, bgh;
+ unsigned int fg_idx, bg_idx;
+ unsigned int fg_rgb, bg_rgb;
+
+ static const unsigned int pc2rgb[8] =
+ { 0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00,
+ 0xffffff
+ };
+
+ /* Converting PC RGBI to sensible RGBA values is an "interesting"
+ proposition. This algorithm may need plenty of tweaking. */
+
+ fga = fg & 0xff000000;
+ fgh = ((fg >> 1) & 0xff000000) | 0x80000000;
+
+ bga = bg & 0xff000000;
+ bgh = ((bg >> 1) & 0xff000000) | 0x80000000;
+
+ for (i = 0; i < 256; i++) {
+ fg_idx = i & 15;
+ bg_idx = i >> 4;
+
+ fg_rgb = pc2rgb[fg_idx & 7] & fg;
+ bg_rgb = pc2rgb[bg_idx & 7] & bg;
+
+ if (fg_idx & 8) {
+ /* High intensity foreground */
+ fg_rgb |= fgh;
+ } else {
+ fg_rgb |= fga;
+ }
+
+ if (bg_idx == 0) {
+ /* Default black background, assume transparent */
+ bg_rgb = 0;
+ } else if (bg_idx & 8) {
+ bg_rgb |= bgh;
+ } else {
+ bg_rgb |= bga;
+ }
+
+ cp->argb_fg = fg_rgb;
+ cp->argb_bg = bg_rgb;
+ cp->shadow = shadow;
+ cp++;
+ }
+}
+
+struct color_table *default_color_table(void)
+{
+ unsigned int i;
+ const struct color_table *dp;
+ struct color_table *cp;
+ struct color_table *color_table;
+ static const int pc2ansi[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+ static char msg_names[6 * 256];
+ char *mp;
+
+ color_table = calloc(NCOLORS + 256, sizeof(struct color_table));
+
+ dp = default_colors;
+ cp = color_table;
+
+ for (i = 0; i < NCOLORS; i++) {
+ *cp = *dp;
+ cp->ansi = refstrdup(dp->ansi);
+ cp++;
+ dp++;
+ }
+
+ mp = msg_names;
+ for (i = 0; i < 256; i++) {
+ cp->name = mp;
+ mp += sprintf(mp, "msg%02x", i) + 1;
+
+ rsprintf(&cp->ansi, "%s3%d;4%d", (i & 8) ? "1;" : "",
+ pc2ansi[i & 7], pc2ansi[(i >> 4) & 7]);
+ cp++;
+ }
+
+ /*** XXX: This needs to move to run_menu() ***/
+ console_color_table = color_table;
+ console_color_table_size = NCOLORS + 256;
+
+ set_msg_colors_global(color_table, MSG_COLORS_DEF_FG,
+ MSG_COLORS_DEF_BG, MSG_COLORS_DEF_SHADOW);
+
+ return color_table;
+}
+
+struct color_table *copy_color_table(const struct color_table *master)
+{
+ const struct color_table *dp;
+ struct color_table *color_table, *cp;
+ unsigned int i;
+
+ color_table = calloc(NCOLORS + 256, sizeof(struct color_table));
+
+ dp = master;
+ cp = color_table;
+
+ for (i = 0; i < NCOLORS + 256; i++) {
+ *cp = *dp;
+ cp->ansi = refstr_get(dp->ansi);
+ cp++;
+ dp++;
+ }
+
+ return color_table;
+}
diff --git a/com32/elflink/ldlinux/config.h b/com32/elflink/ldlinux/config.h
new file mode 100644
index 00000000..242b7dc5
--- /dev/null
+++ b/com32/elflink/ldlinux/config.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 Intel Corporation - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ */
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+/*
+ * These values correspond to the "default" and "ui" commands
+ * respectively. "ui" takes precendence over "default".
+ */
+#define LEVEL_DEFAULT 1
+#define LEVEL_UI 2
+
+extern short uappendlen; //bytes in append= command
+extern short ontimeoutlen; //bytes in ontimeout command
+extern short onerrorlen; //bytes in onerror command
+extern short forceprompt; //force prompt
+extern short noescape; //no escape
+extern short nocomplete; //no label completion on TAB key
+extern short allowimplicit; //allow implicit kernels
+extern short allowoptions; //user-specified options allowed
+extern short includelevel; //nesting level
+extern short defaultlevel; //the current level of default
+extern short vkernel; //have we seen any "label" statements?
+extern short displaycon; //conio.inc
+extern short nohalt; //idle.inc
+
+extern const char *default_cmd; //"default" command line
+extern const char *onerror; //"onerror" command line
+extern const char *ontimeout; //"ontimeout" command line
+
+extern void cat_help_file(int key);
+extern struct menu_entry *find_label(const char *str);
+extern void print_labels(const char *prefix, size_t len);
+
+extern int new_linux_kernel(char *okernel, char *ocmdline);
+
+extern void pm_load_high(com32sys_t *regs);
+
+extern void ldlinux_enter_command(void);
+extern void ldlinux_console_init(void);
+extern const char *apply_extension(const char *kernel, const char *ext);
+
+#endif /* __CONFIG_H__ */
diff --git a/com32/elflink/ldlinux/execute.c b/com32/elflink/ldlinux/execute.c
new file mode 100644
index 00000000..bf0bd8ce
--- /dev/null
+++ b/com32/elflink/ldlinux/execute.c
@@ -0,0 +1,174 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dprintf.h>
+
+#include <com32.h>
+#include <sys/exec.h>
+#include <sys/io.h>
+#include <sys/module.h>
+#include "core.h"
+#include "menu.h"
+#include "fs.h"
+#include "config.h"
+#include "localboot.h"
+#include "bios.h"
+
+#include <syslinux/bootrm.h>
+#include <syslinux/movebits.h>
+#include <syslinux/config.h>
+#include <syslinux/boot.h>
+
+const struct image_types image_boot_types[] = {
+ { "localboot", IMAGE_TYPE_LOCALBOOT },
+ { "kernel", IMAGE_TYPE_KERNEL },
+ { "linux", IMAGE_TYPE_LINUX },
+ { "boot", IMAGE_TYPE_BOOT },
+ { "bss", IMAGE_TYPE_BSS },
+ { "pxe", IMAGE_TYPE_PXE },
+ { "fdimage", IMAGE_TYPE_FDIMAGE },
+ { "com32", IMAGE_TYPE_COM32 },
+ { "config", IMAGE_TYPE_CONFIG },
+ { NULL, 0 },
+};
+
+extern int create_args_and_load(char *);
+
+__export void execute(const char *cmdline, uint32_t type, bool sysappend)
+{
+ const char *kernel, *args;
+ const char *p;
+ com32sys_t ireg;
+ char *q, ch;
+
+ memset(&ireg, 0, sizeof ireg);
+
+ if (strlen(cmdline) >= MAX_CMDLINE_LEN) {
+ printf("cmdline too long\n");
+ return;
+ }
+
+ q = malloc(MAX_CMDLINE_LEN);
+ if (!q) {
+ printf("%s(): Fail to malloc a buffer to exec %s\n",
+ __func__, cmdline);
+ return;
+ }
+
+ kernel = q;
+ p = cmdline;
+ while (*p && !my_isspace(*p))
+ *q++ = *p++;
+ *q++ = '\0';
+
+ args = q;
+ while (*p && my_isspace(*p))
+ p++;
+
+ do {
+ *q++ = ch = *p++;
+ } while (ch);
+
+ if (sysappend) {
+ /* If we've seen some args, insert a space */
+ if (--q != args)
+ *q++ = ' ';
+
+ do_sysappend(q);
+ }
+
+ dprintf("kernel is %s, args = %s type = %d \n", kernel, args, type);
+
+ if (kernel[0] == '.') {
+ /* It might be a type specifier */
+ const struct image_types *t;
+ for (t = image_boot_types; t->name; t++) {
+ if (!strcmp(kernel + 1, t->name)) {
+ /*
+ * Strip the type specifier, apply the
+ * filename extension if COM32 and
+ * retry.
+ */
+ p = args;
+ if (t->type == IMAGE_TYPE_COM32) {
+ p = apply_extension(p, ".c32");
+ if (!p)
+ return;
+ }
+
+ execute(p, t->type, sysappend);
+ return;
+ }
+ }
+ }
+
+ if (type == IMAGE_TYPE_COM32) {
+ /*
+ * We may be called with the console in an unknown
+ * state, so initialise it.
+ */
+ ldlinux_console_init();
+
+ /* new entry for elf format c32 */
+ if (create_args_and_load((char *)cmdline))
+ printf("Failed to load COM32 file %s\n", kernel);
+
+ /*
+ * The old COM32 module code would run the module then
+ * drop the user back at the command prompt,
+ * irrespective of how the COM32 module was loaded,
+ * e.g. from vesamenu.c32.
+ */
+ unload_modules_since("ldlinux.c32");
+
+ /* Restore the console */
+ ldlinux_console_init();
+
+ ldlinux_enter_command();
+ } else if (type == IMAGE_TYPE_CONFIG) {
+ char *argv[] = { "ldlinux.c32", NULL, NULL };
+ char *config;
+ int rv;
+
+ /* kernel contains the config file name */
+ config = malloc(FILENAME_MAX);
+ if (!config)
+ goto out;
+
+ realpath(config, kernel, FILENAME_MAX);
+
+ /* If we got anything on the command line, do a chdir */
+ if (*args)
+ mangle_name(config_cwd, args);
+
+ argv[1] = config;
+ rv = start_ldlinux(2, argv);
+ printf("Failed to exec ldlinux.c32: %s\n", strerror(rv));
+ } else if (type == IMAGE_TYPE_LOCALBOOT) {
+ local_boot(strtoul(kernel, NULL, 0));
+ } else if (type == IMAGE_TYPE_PXE || type == IMAGE_TYPE_BSS ||
+ type == IMAGE_TYPE_BOOT) {
+ chainboot_file(kernel, type);
+ } else {
+ /* Need add one item for kernel load, as we don't use
+ * the assembly runkernel.inc any more */
+ new_linux_kernel((char *)kernel, (char *)args);
+ }
+
+out:
+ free((void *)kernel);
+
+ /* If this returns, something went bad; return to menu */
+}
diff --git a/com32/libutil/get_key.c b/com32/elflink/ldlinux/get_key.c
index f277b43b..cece0f81 100644
--- a/com32/libutil/get_key.c
+++ b/com32/elflink/ldlinux/get_key.c
@@ -41,6 +41,7 @@
#include <sys/times.h>
#include <getkey.h>
#include <libutil.h>
+#include <sys/file.h>
struct keycode {
int code;
@@ -48,7 +49,6 @@ struct keycode {
const unsigned char *seq;
};
-#define MAXLEN 8
#define CODE(x,y) { x, (sizeof y)-1, (const unsigned char *)(y) }
static const struct keycode keycodes[] = {
@@ -118,14 +118,62 @@ static const struct keycode keycodes[] = {
#define KEY_TIMEOUT ((CLK_TCK+9)/10)
-int get_key(FILE * f, clock_t timeout)
+/*
+ * Attempt to decode the key sequence in 'buffer'.
+ *
+ * On success (the data in 'buffer' matches a key code) put the
+ * corresponding key code in 'code' and return 0. Return 1 if 'buffer'
+ * partially matches a key code, i.e. we need more data before we can
+ * make an unambiguous match. Return -1 if the buffer does not contain
+ * a key code.
+ */
+int get_key_decode(char *buffer, int nc, int *code)
{
- unsigned char buffer[MAXLEN];
- int nc, i, rv;
const struct keycode *kc;
+ int i, rv;
+
+ rv = -1;
+ for (i = 0, kc = keycodes; i < NCODES; i++, kc++) {
+ if (nc == kc->seqlen && !memcmp(buffer, kc->seq, nc)) {
+ *code = kc->code;
+ rv = 0;
+ break;
+ } else if (nc < kc->seqlen && !memcmp(buffer, kc->seq, nc)) {
+ rv = 1;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+#ifdef __COM32__
+extern ssize_t __rawcon_read(struct file_info *fp, void *buf, size_t count);
+
+int raw_read(int fd, void *buf, size_t count)
+{
+ (void)fd;
+
+ /*
+ * Instead of using the read(2) stdlib function use
+ * __rawcon_read() directly since we want a single key and
+ * don't want any processing/batching of the user input to
+ * occur - we want the raw data.
+ */
+ return __rawcon_read(NULL, buf, count);
+}
+#else
+extern int raw_read(int fd, void *buf, size_t count);
+#endif
+
+__export int get_key(FILE * f, clock_t timeout)
+{
+ char buffer[KEY_MAXLEN];
+ int nc, rv;
int another;
- unsigned char ch;
+ char ch;
clock_t start;
+ int code;
/* We typically start in the middle of a clock tick */
if (timeout)
@@ -134,12 +182,12 @@ int get_key(FILE * f, clock_t timeout)
nc = 0;
start = times(NULL);
do {
- rv = read(fileno(f), &ch, 1);
+ rv = raw_read(fileno(f), &ch, 1);
if (rv == 0 || (rv == -1 && errno == EAGAIN)) {
clock_t lateness = times(NULL) - start;
if (nc && lateness > 1 + KEY_TIMEOUT) {
if (nc == 1)
- return buffer[0]; /* timeout in sequence */
+ return (unsigned char)buffer[0]; /* timeout */
else if (timeout && lateness > timeout)
return KEY_NONE;
} else if (!nc && timeout && lateness > timeout)
@@ -156,17 +204,15 @@ int get_key(FILE * f, clock_t timeout)
buffer[nc++] = ch;
another = 0;
- for (i = 0, kc = keycodes; i < NCODES; i++, kc++) {
- if (nc == kc->seqlen && !memcmp(buffer, kc->seq, nc))
- return kc->code;
- else if (nc < kc->seqlen && !memcmp(buffer, kc->seq, nc)) {
+ rv = get_key_decode(buffer, nc, &code);
+ if (!rv)
+ return code;
+ else if (rv == 1)
another = 1;
- break;
- }
- }
+
} while (another);
/* We got an unrecognized sequence; return the first character */
/* We really should remember this and return subsequent characters later */
- return buffer[0];
+ return (unsigned char)buffer[0];
}
diff --git a/com32/lib/syslinux/getadv.c b/com32/elflink/ldlinux/getadv.c
index 5578313e..1c27f1b8 100644
--- a/com32/lib/syslinux/getadv.c
+++ b/com32/elflink/ldlinux/getadv.c
@@ -36,7 +36,7 @@
#include <klibc/compiler.h>
#include <inttypes.h>
-const void *syslinux_getadv(int tag, size_t * size)
+__export const void *syslinux_getadv(int tag, size_t * size)
{
const uint8_t *p;
size_t left;
diff --git a/com32/elflink/ldlinux/kernel.c b/com32/elflink/ldlinux/kernel.c
new file mode 100644
index 00000000..f3ba37fa
--- /dev/null
+++ b/com32/elflink/ldlinux/kernel.c
@@ -0,0 +1,131 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <console.h>
+#include <dprintf.h>
+#include <syslinux/loadfile.h>
+#include <syslinux/linux.h>
+#include <syslinux/pxe.h>
+#include "core.h"
+
+const char *globaldefault = NULL;
+const char *append = NULL;
+
+/* Will be called from readconfig.c */
+int new_linux_kernel(char *okernel, char *ocmdline)
+{
+ const char *kernel_name = NULL, *args = NULL;
+ struct initramfs *initramfs = NULL;
+ char *temp;
+ void *kernel_data;
+ size_t kernel_len, cmdline_len;
+ bool opt_quiet = false;
+ char *initrd_name, *cmdline;
+
+ dprintf("okernel = %s, ocmdline = %s", okernel, ocmdline);
+
+ if (okernel)
+ kernel_name = okernel;
+ else if (globaldefault)
+ kernel_name = globaldefault;
+
+ if (ocmdline)
+ args = ocmdline;
+ else if (append)
+ args = append;
+
+ cmdline_len = strlen("BOOT_IMAGE=") + strlen(kernel_name);
+ cmdline_len += 1; /* space between BOOT_IMAGE and args */
+ cmdline_len += strlen(args);
+ cmdline_len += 1; /* NUL-termination */
+
+ cmdline = malloc(cmdline_len);
+ if (!cmdline) {
+ printf("Failed to alloc memory for cmdline\n");
+ return 1;
+ }
+
+ sprintf(cmdline, "BOOT_IMAGE=%s %s", kernel_name, args);
+
+ /* "keeppxe" handling */
+#if IS_PXELINUX
+ extern char KeepPXE;
+
+ if (strstr(cmdline, "keeppxe"))
+ KeepPXE |= 1;
+#endif
+
+ if (strstr(cmdline, "quiet"))
+ opt_quiet = true;
+
+ if (!opt_quiet)
+ printf("Loading %s... ", kernel_name);
+
+ if (loadfile(kernel_name, &kernel_data, &kernel_len)) {
+ if (opt_quiet)
+ printf("Loading %s ", kernel_name);
+ printf("failed: ");
+ goto bail;
+ }
+
+ if (!opt_quiet)
+ printf("ok\n");
+
+ /* Find and load initramfs */
+ temp = strstr(cmdline, "initrd=");
+ if (temp) {
+ /* Initialize the initramfs chain */
+ initramfs = initramfs_init();
+ if (!initramfs)
+ goto bail;
+
+ temp += 6; /* strlen("initrd") */
+ do {
+ size_t n = 0;
+ char *p;
+
+ temp++; /* Skip = or , */
+
+ p = temp;
+ while (*p != ' ' && *p != ',' && *p) {
+ p++;
+ n++;
+ }
+
+ initrd_name = malloc(n + 1);
+ if (!initrd_name) {
+ printf("Failed to allocate space for initrd\n");
+ goto bail;
+ }
+
+ snprintf(initrd_name, n + 1, "%s", temp);
+ temp += n;
+
+ if (!opt_quiet)
+ printf("Loading %s...", initrd_name);
+
+ if (initramfs_load_archive(initramfs, initrd_name)) {
+ if (opt_quiet)
+ printf("Loading %s ", initrd_name);
+ free(initrd_name);
+ printf("failed: ");
+ goto bail;
+ }
+
+ free(initrd_name);
+
+ if (!opt_quiet)
+ printf("ok\n");
+ } while (*temp == ',');
+ }
+
+ /* This should not return... */
+ syslinux_boot_linux(kernel_data, kernel_len, initramfs, NULL, cmdline);
+ printf("Booting kernel failed: ");
+
+bail:
+ free(cmdline);
+ printf("%s\n", strerror(errno));
+ return 1;
+}
diff --git a/com32/elflink/ldlinux/ldlinux.c b/com32/elflink/ldlinux/ldlinux.c
new file mode 100644
index 00000000..76d117c7
--- /dev/null
+++ b/com32/elflink/ldlinux/ldlinux.c
@@ -0,0 +1,349 @@
+#include <linux/list.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include "cli.h"
+#include "console.h"
+#include "com32.h"
+#include "menu.h"
+#include "config.h"
+#include "syslinux/adv.h"
+#include "syslinux/boot.h"
+#include "syslinux/config.h"
+
+#include <sys/module.h>
+
+struct file_ext {
+ const char *name;
+ enum kernel_type type;
+};
+
+static const struct file_ext file_extensions[] = {
+ { ".c32", IMAGE_TYPE_COM32 },
+ { ".img", IMAGE_TYPE_FDIMAGE },
+ { ".bss", IMAGE_TYPE_BSS },
+ { ".bin", IMAGE_TYPE_BOOT },
+ { ".bs", IMAGE_TYPE_BOOT },
+ { ".0", IMAGE_TYPE_PXE },
+ { NULL, 0 },
+};
+
+/*
+ * Return a pointer to one byte after the last character of the
+ * command.
+ */
+static inline const char *find_command(const char *str)
+{
+ const char *p;
+
+ p = str;
+ while (*p && !my_isspace(*p))
+ p++;
+ return p;
+}
+
+__export uint32_t parse_image_type(const char *kernel)
+{
+ const struct file_ext *ext;
+ const char *p;
+ int len;
+
+ /* Find the end of the command */
+ p = find_command(kernel);
+ len = p - kernel;
+
+ for (ext = file_extensions; ext->name; ext++) {
+ int elen = strlen(ext->name);
+
+ if (!strncmp(kernel + len - elen, ext->name, elen))
+ return ext->type;
+ }
+
+ /* use IMAGE_TYPE_KERNEL as default */
+ return IMAGE_TYPE_KERNEL;
+}
+
+/*
+ * Returns the kernel name with file extension if one wasn't present.
+ */
+static const char *get_extension(const char *kernel)
+{
+ const struct file_ext *ext;
+ const char *p;
+ int len;
+
+ /* Find the end of the command */
+ p = find_command(kernel);
+ len = p - kernel;
+
+ for (ext = file_extensions; ext->name; ext++) {
+ char *str;
+ int elen = strlen(ext->name);
+ FILE *f;
+
+ str = malloc(len + elen + 1);
+
+ strncpy(str, kernel, len);
+ strncpy(str + len, ext->name, elen);
+ str[len + elen] = '\0';
+ f = findpath(str);
+ free(str);
+
+ if (f) {
+ fclose(f);
+ return ext->name;
+ }
+ }
+
+ return NULL;
+}
+
+const char *apply_extension(const char *kernel, const char *ext)
+{
+ const char *p;
+ char *k;
+ int len = strlen(kernel);
+ int elen = strlen(ext);
+
+ k = malloc(len + elen + 1);
+ if (!k)
+ return NULL;
+
+ p = find_command(kernel);
+
+ len = p - kernel;
+
+ /* Copy just the kernel name */
+ memcpy(k, kernel, len);
+
+ /* Append the extension */
+ if (strncmp(p - elen, ext, elen)) {
+ memcpy(k + len, ext, elen);
+ len += elen;
+ }
+
+ /* Copy the rest of the command line */
+ strcpy(k + len, p);
+
+ k[len + strlen(p)] = '\0';
+
+ return k;
+}
+
+/*
+ * Attempt to load a kernel after deciding what type of image it is.
+ *
+ * We only return from this function if something went wrong loading
+ * the the kernel. If we return the caller should call enter_cmdline()
+ * so that the user can help us out.
+ */
+__export void load_kernel(const char *command_line)
+{
+ struct menu_entry *me;
+ const char *cmdline;
+ const char *kernel;
+ uint32_t type;
+
+ kernel = strdup(command_line);
+ if (!kernel)
+ goto bad_kernel;
+
+ /* Virtual kernel? */
+ me = find_label(kernel);
+ if (me) {
+ const char *args;
+ char *cmd;
+ size_t len = strlen(me->cmdline) + 1;
+
+ /* Find the end of the command */
+ args = find_command(kernel);
+ while(*args && my_isspace(*args))
+ args++;
+
+ if (strlen(args))
+ len += strlen(args) + 1; /* +1 for space (' ') */
+
+ cmd = malloc(len);
+ if (!cmd)
+ goto bad_kernel;
+
+ if (strlen(args))
+ snprintf(cmd, len, "%s %s", me->cmdline, args);
+ else
+ strncpy(cmd, me->cmdline, len);
+
+ type = parse_image_type(cmd);
+ execute(cmd, type, false);
+ /* We shouldn't return */
+ goto bad_kernel;
+ }
+
+ if (!allowimplicit)
+ goto bad_implicit;
+
+ /* Insert a null character to ignore any user-specified options */
+ if (!allowoptions) {
+ char *p = (char *)find_command(kernel);
+ *p = '\0';
+ }
+
+ type = parse_image_type(kernel);
+ if (type == IMAGE_TYPE_KERNEL) {
+ const char *ext;
+
+ /*
+ * Automatically lookup the extension if one wasn't
+ * supplied by the user.
+ */
+ ext = get_extension(kernel);
+ if (ext) {
+ const char *k;
+
+ k = apply_extension(kernel, ext);
+ if (!k)
+ goto bad_kernel;
+
+ free((void *)kernel);
+ kernel = k;
+
+ type = parse_image_type(kernel);
+ }
+ }
+
+ execute(kernel, type, true);
+ free((void *)kernel);
+
+bad_implicit:
+bad_kernel:
+ /*
+ * If we fail to boot the kernel execute the "onerror" command
+ * line.
+ */
+ if (onerrorlen) {
+ me = find_label(onerror);
+ if (me)
+ rsprintf(&cmdline, "%s %s", me->cmdline, default_cmd);
+ else
+ rsprintf(&cmdline, "%s %s", onerror, default_cmd);
+
+ type = parse_image_type(cmdline);
+ execute(cmdline, type, true);
+ }
+}
+
+/*
+ * If this function returns you must call ldinux_enter_command() to
+ * preserve the 4.0x behaviour.
+ */
+void ldlinux_auto_boot(void)
+{
+ if (!defaultlevel) {
+ if (strlen(ConfigName))
+ printf("No DEFAULT or UI configuration directive found!\n");
+ if (noescape)
+ kaboom();
+ } else
+ load_kernel(default_cmd);
+}
+
+static void enter_cmdline(void)
+{
+ const char *cmdline;
+
+ /* Enter endless command line prompt, should support "exit" */
+ while (1) {
+ bool to = false;
+
+ if (noescape) {
+ ldlinux_auto_boot();
+ continue;
+ }
+
+ cmdline = edit_cmdline("boot:", 1, NULL, cat_help_file, &to);
+ printf("\n");
+
+ /* return if user only press enter or we timed out */
+ if (!cmdline || cmdline[0] == '\0') {
+ if (to && ontimeoutlen)
+ load_kernel(ontimeout);
+ else
+ ldlinux_auto_boot();
+ } else
+ load_kernel(cmdline);
+ }
+}
+
+void ldlinux_enter_command(void)
+{
+ enter_cmdline();
+}
+
+/*
+ * Undo the work we did in openconsole().
+ */
+static void __destructor close_console(void)
+{
+ int i;
+
+ for (i = 0; i <= 2; i++)
+ close(i);
+}
+
+void ldlinux_console_init(void)
+{
+ openconsole(&dev_stdcon_r, &dev_ansiserial_w);
+}
+
+__export int main(int argc __unused, char **argv)
+{
+ const void *adv;
+ const char *cmdline;
+ size_t count = 0;
+
+ ldlinux_console_init();
+
+ parse_configs(&argv[1]);
+
+ __syslinux_set_serial_console_info();
+
+ adv = syslinux_getadv(ADV_BOOTONCE, &count);
+ if (adv && count) {
+ /*
+ * We apparently have a boot-once set; clear it and
+ * then execute the boot-once.
+ */
+ char *src, *dst;
+ size_t i;
+
+ src = (char *)adv;
+ cmdline = dst = malloc(count + 1);
+ if (!dst) {
+ printf("Failed to allocate memory for ADV\n");
+ ldlinux_enter_command();
+ }
+
+ for (i = 0; i < count; i++)
+ *dst++ = *src++;
+ *dst = '\0'; /* Null-terminate */
+
+ /* Clear the boot-once data from the ADV */
+ if (!syslinux_setadv(ADV_BOOTONCE, 0, NULL))
+ syslinux_adv_write();
+
+ load_kernel(cmdline); /* Shouldn't return */
+ ldlinux_enter_command();
+ }
+
+ /* TODO: Check KbdFlags? */
+ if (!forceprompt)
+ ldlinux_auto_boot();
+
+ if (defaultlevel > 1)
+ ldlinux_auto_boot();
+
+ ldlinux_enter_command();
+ return 0;
+}
diff --git a/core/fs/loadhigh.c b/com32/elflink/ldlinux/loadhigh.c
index bd9d3535..0f2f8428 100644
--- a/core/fs/loadhigh.c
+++ b/com32/elflink/ldlinux/loadhigh.c
@@ -37,7 +37,7 @@
#include "core.h"
#include "fs.h"
-#define MAX_CHUNK (1 << 20) /* 1 MB */
+#define MAX_CHUNK (1UL << 20) /* 1 MB */
void pm_load_high(com32sys_t *regs)
{
diff --git a/com32/elflink/ldlinux/msg.c b/com32/elflink/ldlinux/msg.c
new file mode 100644
index 00000000..9ded33ef
--- /dev/null
+++ b/com32/elflink/ldlinux/msg.c
@@ -0,0 +1,228 @@
+#include <com32.h>
+#include <stdio.h>
+#include <bios.h>
+#include <graphics.h>
+
+static uint8_t TextAttribute; /* Text attribute for message file */
+extern uint8_t DisplayMask; /* Display modes mask */
+
+/* Routine to interpret next print char */
+static void (*NextCharJump)(uint8_t);
+
+void msg_initvars(void);
+static void msg_setfg(uint8_t data);
+static void msg_putchar(uint8_t ch);
+
+/*
+ *
+ * get_msg_file: Load a text file and write its contents to the screen,
+ * interpreting color codes.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int get_msg_file(char *filename)
+{
+ FILE *f;
+ char ch;
+
+ f = fopen(filename, "r");
+ if (!f)
+ return -1;
+
+ TextAttribute = 0x7; /* Default grey on white */
+ DisplayMask = 0x7; /* Display text in all modes */
+ msg_initvars();
+
+ /*
+ * Read the text file a byte at a time and interpret that
+ * byte.
+ */
+ while ((ch = getc(f)) != EOF) {
+ /* DOS EOF? */
+ if (ch == 0x1A)
+ break;
+
+ NextCharJump(ch); /* Do what shall be done */
+ }
+
+ DisplayMask = 0x07;
+
+ fclose(f);
+ return 0;
+}
+
+static inline int display_mask_vga(void)
+{
+ uint8_t mask = UsingVGA & 0x1;
+ return (DisplayMask & ++mask);
+}
+
+static void msg_setbg(uint8_t data)
+{
+ if (unhexchar(&data) == 0) {
+ data <<= 4;
+ if (display_mask_vga())
+ TextAttribute = data;
+
+ NextCharJump = msg_setfg;
+ } else {
+ TextAttribute = 0x7; /* Default attribute */
+ NextCharJump = msg_putchar;
+ }
+}
+
+static void msg_setfg(uint8_t data)
+{
+ if (unhexchar(&data) == 0) {
+ if (display_mask_vga()) {
+ /* setbg set foreground to 0 */
+ TextAttribute |= data;
+ }
+ } else
+ TextAttribute = 0x7; /* Default attribute */
+
+ NextCharJump = msg_putchar;
+}
+
+static inline void msg_ctrl_o(void)
+{
+ NextCharJump = msg_setbg;
+}
+
+/* Convert ANSI colors to PC display attributes */
+static int convert_to_pcdisplay[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+static void set_fgbg(void)
+{
+ uint8_t bg, fg;
+
+ fg = convert_to_pcdisplay[(TextAttribute & 0x7)];
+ bg = convert_to_pcdisplay[((TextAttribute >> 4) & 0x7)];
+
+ printf("\033[");
+ if (TextAttribute & 0x8)
+ printf("1;"); /* Foreground bright */
+
+ printf("3%dm\033[", fg);
+
+ if (TextAttribute & 0x80)
+ printf("5;"); /* Foreground blink */
+
+ printf("4%dm", bg);
+}
+
+static void msg_formfeed(void)
+{
+ set_fgbg();
+ printf("\033[2J\033[H\033[0m");
+}
+
+static void msg_novga(void)
+{
+ syslinux_force_text_mode();
+ msg_initvars();
+}
+
+static void msg_viewimage(void)
+{
+ FILE *f;
+
+ *VGAFilePtr = '\0'; /* Zero-terminate filename */
+
+ mangle_name(VGAFileMBuf, VGAFileBuf);
+ f = fopen(VGAFileMBuf, "r");
+ if (!f) {
+ /* Not there */
+ NextCharJump = msg_putchar;
+ return;
+ }
+
+ vgadisplayfile(f);
+ fclose(f);
+ msg_initvars();
+}
+
+/*
+ * Getting VGA filename
+ */
+static void msg_filename(uint8_t data)
+{
+ /* <LF> = end of filename */
+ if (data == 0x0A) {
+ msg_viewimage();
+ return;
+ }
+
+ /* Ignore space/control char */
+ if (data > ' ') {
+ if ((char *)VGAFilePtr < (VGAFileBuf + sizeof(VGAFileBuf)))
+ *VGAFilePtr++ = data;
+ }
+}
+
+static void msg_vga(void)
+{
+ NextCharJump = msg_filename;
+ VGAFilePtr = (uint16_t *)VGAFileBuf;
+}
+
+static void msg_normal(uint8_t data)
+{
+ /* 0x1 = text mode, 0x2 = graphics mode */
+ if (!display_mask_vga() || !(DisplayCon & 0x01)) {
+ /* Write to serial port */
+ if (DisplayMask & 0x4)
+ write_serial(data);
+
+ return; /* Not screen */
+ }
+
+ set_fgbg();
+ printf("%c\033[0m", data);
+}
+
+static void msg_modectl(uint8_t data)
+{
+ data &= 0x07;
+ DisplayMask = data;
+ NextCharJump = msg_putchar;
+}
+
+static void msg_putchar(uint8_t ch)
+{
+ /* 10h to 17h are mode controls */
+ if (ch >= 0x10 && ch < 0x18) {
+ msg_modectl(ch);
+ return;
+ }
+
+ switch (ch) {
+ case 0x0F: /* ^O = color code follows */
+ msg_ctrl_o();
+ break;
+ case 0x0D: /* Ignore <CR> */
+ break;
+ case 0x0C: /* <FF> = clear screen */
+ msg_formfeed();
+ break;
+ case 0x19: /* <EM> = return to text mode */
+ msg_novga();
+ break;
+ case 0x18: /* <CAN> = VGA filename follows */
+ msg_vga();
+ break;
+ default:
+ msg_normal(ch);
+ break;
+ }
+}
+
+/*
+ * Subroutine to initialize variables, also needed after loading
+ * graphics file.
+ */
+void msg_initvars(void)
+{
+ /* Initialize state machine */
+ NextCharJump = msg_putchar;
+}
diff --git a/com32/elflink/ldlinux/readconfig.c b/com32/elflink/ldlinux/readconfig.c
new file mode 100644
index 00000000..22efbe43
--- /dev/null
+++ b/com32/elflink/ldlinux/readconfig.c
@@ -0,0 +1,1572 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2013 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <sys/io.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <minmax.h>
+#include <alloca.h>
+#include <inttypes.h>
+#include <colortbl.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <syslinux/config.h>
+#include <dprintf.h>
+#include <ctype.h>
+#include <bios.h>
+#include <core.h>
+#include <fs.h>
+#include <syslinux/pxe_api.h>
+
+#include "menu.h"
+#include "config.h"
+#include "getkey.h"
+#include "core.h"
+#include "fs.h"
+
+const struct menu_parameter mparm[NPARAMS] = {
+ [P_WIDTH] = {"width", 0},
+ [P_MARGIN] = {"margin", 10},
+ [P_PASSWD_MARGIN] = {"passwordmargin", 3},
+ [P_MENU_ROWS] = {"rows", 12},
+ [P_TABMSG_ROW] = {"tabmsgrow", 18},
+ [P_CMDLINE_ROW] = {"cmdlinerow", 18},
+ [P_END_ROW] = {"endrow", -1},
+ [P_PASSWD_ROW] = {"passwordrow", 11},
+ [P_TIMEOUT_ROW] = {"timeoutrow", 20},
+ [P_HELPMSG_ROW] = {"helpmsgrow", 22},
+ [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1},
+ [P_HSHIFT] = {"hshift", 0},
+ [P_VSHIFT] = {"vshift", 0},
+ [P_HIDDEN_ROW] = {"hiddenrow", -2},
+};
+
+/* Must match enum kernel_type */
+static const char *const kernel_types[] = {
+ "none",
+ "localboot",
+ "kernel",
+ "linux",
+ "boot",
+ "bss",
+ "pxe",
+ "fdimage",
+ "comboot",
+ "com32",
+ "config",
+ NULL
+};
+
+short uappendlen = 0; //bytes in append= command
+short ontimeoutlen = 0; //bytes in ontimeout command
+short onerrorlen = 0; //bytes in onerror command
+short forceprompt = 0; //force prompt
+short noescape = 0; //no escape
+short nocomplete = 0; //no label completion on TAB key
+short allowimplicit = 1; //allow implicit kernels
+short allowoptions = 1; //user-specified options allowed
+short includelevel = 1; //nesting level
+short defaultlevel = 0; //the current level of default
+short vkernel = 0; //have we seen any "label" statements?
+extern short NoHalt; //idle.c
+
+const char *onerror = NULL; //"onerror" command line
+const char *ontimeout = NULL; //"ontimeout" command line
+
+__export const char *default_cmd = NULL; //"default" command line
+
+/* Empty refstring */
+const char *empty_string;
+
+/* Root menu, starting menu, hidden menu, and list of all menus */
+struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu;
+
+/* These are global parameters regardless of which menu we're displaying */
+int shiftkey = 0; /* Only display menu if shift key pressed */
+int hiddenmenu = 0;
+long long totaltimeout = 0;
+unsigned int kbdtimeout = 0;
+
+/* Keep track of global default */
+static int has_ui = 0; /* DEFAULT only counts if UI is found */
+extern const char *globaldefault;
+static bool menusave = false; /* True if there is any "menu save" */
+
+/* Linked list of all entires, hidden or not; used by unlabel() */
+static struct menu_entry *all_entries;
+static struct menu_entry **all_entries_end = &all_entries;
+
+static const struct messages messages[MSG_COUNT] = {
+ [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."},
+ [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"},
+ [MSG_NOTAB] = {"notabmsg", ""},
+ [MSG_PASSPROMPT] = {"passprompt", "Password required"},
+};
+
+#define astrdup(x) ({ char *__x = (x); \
+ size_t __n = strlen(__x) + 1; \
+ char *__p = alloca(__n); \
+ if ( __p ) memcpy(__p, __x, __n); \
+ __p; })
+
+/*
+ * Search the list of all menus for a specific label
+ */
+static struct menu *find_menu(const char *label)
+{
+ struct menu *m;
+
+ for (m = menu_list; m; m = m->next) {
+ if (!strcmp(label, m->label))
+ return m;
+ }
+
+ return NULL;
+}
+
+#define MAX_LINE 4096
+
+/* Strip ^ from a string, returning a new reference to the same refstring
+ if none present */
+static const char *strip_caret(const char *str)
+{
+ const char *p, *r;
+ char *q;
+ int carets = 0;
+
+ p = str;
+ for (;;) {
+ p = strchr(p, '^');
+ if (!p)
+ break;
+ carets++;
+ p++;
+ }
+
+ if (!carets)
+ return refstr_get(str);
+
+ r = q = refstr_alloc(strlen(str) - carets);
+ for (p = str; *p; p++)
+ if (*p != '^')
+ *q++ = *p;
+
+ *q = '\0'; /* refstr_alloc() already did this... */
+
+ return r;
+}
+
+/* Check to see if we are at a certain keyword (case insensitive) */
+/* Returns a pointer to the first character past the keyword */
+static char *looking_at(char *line, const char *kwd)
+{
+ char *p = line;
+ const char *q = kwd;
+
+ while (*p && *q && ((*p ^ *q) & ~0x20) == 0) {
+ p++;
+ q++;
+ }
+
+ if (*q)
+ return NULL; /* Didn't see the keyword */
+
+ return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
+}
+
+static struct menu *new_menu(struct menu *parent,
+ struct menu_entry *parent_entry, const char *label)
+{
+ struct menu *m = calloc(1, sizeof(struct menu));
+ int i;
+
+ //dprintf("enter: menu_label = %s", label);
+
+ m->label = label;
+ m->title = refstr_get(empty_string);
+
+ if (parent) {
+ /* Submenu */
+ m->parent = parent;
+ m->parent_entry = parent_entry;
+ parent_entry->action = MA_SUBMENU;
+ parent_entry->submenu = m;
+
+ for (i = 0; i < MSG_COUNT; i++)
+ m->messages[i] = refstr_get(parent->messages[i]);
+
+ memcpy(m->mparm, parent->mparm, sizeof m->mparm);
+
+ m->allowedit = parent->allowedit;
+ m->timeout = parent->timeout;
+ m->save = parent->save;
+
+ m->ontimeout = refstr_get(parent->ontimeout);
+ m->onerror = refstr_get(parent->onerror);
+ m->menu_master_passwd = refstr_get(parent->menu_master_passwd);
+ m->menu_background = refstr_get(parent->menu_background);
+
+ m->color_table = copy_color_table(parent->color_table);
+
+ for (i = 0; i < 12; i++) {
+ m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname);
+ m->fkeyhelp[i].background =
+ refstr_get(parent->fkeyhelp[i].background);
+ }
+ } else {
+ /* Root menu */
+ for (i = 0; i < MSG_COUNT; i++)
+ m->messages[i] = refstrdup(messages[i].defmsg);
+ for (i = 0; i < NPARAMS; i++)
+ m->mparm[i] = mparm[i].value;
+
+ m->allowedit = true; /* Allow edits of the command line */
+ m->color_table = default_color_table();
+ }
+
+ m->next = menu_list;
+ menu_list = m;
+
+ return m;
+}
+
+struct labeldata {
+ const char *label;
+ const char *kernel;
+ enum kernel_type type;
+ const char *append;
+ const char *initrd;
+ const char *menulabel;
+ const char *passwd;
+ char *helptext;
+ unsigned int ipappend;
+ unsigned int menuhide;
+ unsigned int menudefault;
+ unsigned int menuseparator;
+ unsigned int menudisabled;
+ unsigned int menuindent;
+ enum menu_action action;
+ int save;
+ struct menu *submenu;
+};
+
+/* Menu currently being parsed */
+static struct menu *current_menu;
+
+static void clear_label_data(struct labeldata *ld)
+{
+ refstr_put(ld->label);
+ refstr_put(ld->kernel);
+ refstr_put(ld->append);
+ refstr_put(ld->initrd);
+ refstr_put(ld->menulabel);
+ refstr_put(ld->passwd);
+
+ memset(ld, 0, sizeof *ld);
+}
+
+static struct menu_entry *new_entry(struct menu *m)
+{
+ struct menu_entry *me;
+
+ //dprintf("enter, call from menu %s", m->label);
+
+ if (m->nentries >= m->nentries_space) {
+ if (!m->nentries_space)
+ m->nentries_space = 1;
+ else
+ m->nentries_space <<= 1;
+
+ m->menu_entries = realloc(m->menu_entries, m->nentries_space *
+ sizeof(struct menu_entry *));
+ }
+
+ me = calloc(1, sizeof(struct menu_entry));
+ me->menu = m;
+ me->entry = m->nentries;
+ m->menu_entries[m->nentries++] = me;
+ *all_entries_end = me;
+ all_entries_end = &me->next;
+
+ return me;
+}
+
+static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
+{
+ const char *p = strchr(me->displayname, '^');
+
+ if (me->action != MA_DISABLED) {
+ if (p && p[1]) {
+ unsigned char hotkey = p[1] & ~0x20;
+ if (!m->menu_hotkeys[hotkey]) {
+ me->hotkey = hotkey;
+ m->menu_hotkeys[hotkey] = me;
+ }
+ }
+ }
+}
+
+/*
+ * Copy a string, converting whitespace characters to underscores
+ * and compacting them. Return a pointer to the final null.
+ */
+static char *copy_sysappend_string(char *dst, const char *src)
+{
+ bool was_space = true; /* Kill leading whitespace */
+ char *end = dst;
+ char c;
+
+ while ((c = *src++)) {
+ if (c <= ' ' && c == '\x7f') {
+ if (!was_space)
+ *dst++ = '_';
+ was_space = true;
+ } else {
+ *dst++ = c;
+ end = dst;
+ was_space = false;
+ }
+ }
+ *end = '\0';
+ return end;
+}
+
+static void record(struct menu *m, struct labeldata *ld, const char *append)
+{
+ int i;
+ struct menu_entry *me;
+ const struct syslinux_ipappend_strings *ipappend;
+
+ if (!ld->label)
+ return; /* Nothing defined */
+
+ /* Hidden entries are recorded on a special "hidden menu" */
+ if (ld->menuhide)
+ m = hide_menu;
+
+ char ipoptions[4096], *ipp;
+ const char *a;
+ char *s;
+
+ me = new_entry(m);
+
+ me->displayname = ld->menulabel
+ ? refstr_get(ld->menulabel) : refstr_get(ld->label);
+ me->label = refstr_get(ld->label);
+ me->passwd = refstr_get(ld->passwd);
+ me->helptext = ld->helptext;
+ me->hotkey = 0;
+ me->action = ld->action ? ld->action : MA_CMD;
+ me->save = ld->save ? (ld->save > 0) : m->save;
+
+ if (ld->menuindent) {
+ const char *dn;
+
+ rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname);
+ refstr_put(me->displayname);
+ me->displayname = dn;
+ }
+
+ if (ld->menuseparator) {
+ refstr_put(me->displayname);
+ me->displayname = refstr_get(empty_string);
+ }
+
+ if (ld->menuseparator || ld->menudisabled) {
+ me->action = MA_DISABLED;
+ refstr_put(me->label);
+ me->label = NULL;
+ refstr_put(me->passwd);
+ me->passwd = NULL;
+ }
+
+ if (ld->menulabel)
+ consider_for_hotkey(m, me);
+
+ switch (me->action) {
+ case MA_CMD:
+ ipp = ipoptions;
+ *ipp = '\0';
+
+ if (ld->initrd)
+ ipp += sprintf(ipp, " initrd=%s", ld->initrd);
+
+ if (ld->ipappend) {
+ ipappend = syslinux_ipappend_strings();
+ for (i = 0; i < ipappend->count; i++) {
+ if ((ld->ipappend & (1U << i)) &&
+ ipappend->ptr[i] && ipappend->ptr[i][0]) {
+ *ipp++ = ' ';
+ ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
+ }
+ }
+ }
+
+ a = ld->append;
+ if (!a)
+ a = append;
+ if (!a || (a[0] == '-' && !a[1]))
+ a = "";
+ s = a[0] ? " " : "";
+
+ if (ld->type == KT_KERNEL)
+ rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions);
+ else
+ rsprintf(&me->cmdline, ".%s %s%s%s%s",
+ kernel_types[ld->type], ld->kernel, s, a, ipoptions);
+ dprintf("type = %s, cmd = %s", kernel_types[ld->type], me->cmdline);
+ break;
+
+ case MA_GOTO_UNRES:
+ case MA_EXIT_UNRES:
+ me->cmdline = refstr_get(ld->kernel);
+ break;
+
+ case MA_GOTO:
+ case MA_EXIT:
+ me->submenu = ld->submenu;
+ break;
+
+ default:
+ break;
+ }
+
+ if (ld->menudefault && me->action == MA_CMD)
+ m->defentry = m->nentries - 1;
+
+ clear_label_data(ld);
+}
+
+static struct menu *begin_submenu(const char *tag)
+{
+ struct menu_entry *me;
+
+ if (!tag[0])
+ tag = NULL;
+
+ me = new_entry(current_menu);
+ me->displayname = refstrdup(tag);
+ return new_menu(current_menu, me, refstr_get(me->displayname));
+}
+
+static struct menu *end_submenu(void)
+{
+ return current_menu->parent ? current_menu->parent : current_menu;
+}
+
+void print_labels(const char *prefix, size_t len)
+{
+ struct menu_entry *me;
+
+ printf("\n");
+ for (me = all_entries; me; me = me->next ) {
+ if (!me->label)
+ continue;
+
+ if (!strncmp(prefix, me->label, len))
+ printf(" %s", me->label);
+ }
+ printf("\n");
+}
+
+struct menu_entry *find_label(const char *str)
+{
+ const char *p;
+ struct menu_entry *me;
+ int pos;
+
+ p = str;
+ while (*p && !my_isspace(*p))
+ p++;
+
+ /* p now points to the first byte beyond the kernel name */
+ pos = p - str;
+
+ for (me = all_entries; me; me = me->next) {
+ if (!strncmp(str, me->label, pos) && !me->label[pos])
+ return me;
+ }
+
+ return NULL;
+}
+
+static const char *unlabel(const char *str)
+{
+ /* Convert a CLI-style command line to an executable command line */
+ const char *p;
+ const char *q;
+ struct menu_entry *me;
+ int pos;
+
+ p = str;
+ while (*p && !my_isspace(*p))
+ p++;
+
+ /* p now points to the first byte beyond the kernel name */
+ pos = p - str;
+
+ for (me = all_entries; me; me = me->next) {
+ if (!strncmp(str, me->label, pos) && !me->label[pos]) {
+ /* Found matching label */
+ rsprintf(&q, "%s%s", me->cmdline, p);
+ refstr_put(str);
+ return q;
+ }
+ }
+
+ return str;
+}
+
+static const char *__refdup_word(char *p, char **ref)
+{
+ char *sp = p;
+ char *ep = sp;
+
+ while (*ep && !my_isspace(*ep))
+ ep++;
+
+ if (ref)
+ *ref = ep;
+ return refstrndup(sp, ep - sp);
+}
+
+static const char *refdup_word(char **p)
+{
+ return __refdup_word(*p, p);
+}
+
+int my_isxdigit(char c)
+{
+ unsigned int uc = c;
+
+ return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
+}
+
+unsigned int hexval(char c)
+{
+ unsigned char uc = c | 0x20;
+ unsigned int v;
+
+ v = uc - '0';
+ if (v < 10)
+ return v;
+
+ return uc - 'a' + 10;
+}
+
+unsigned int hexval2(const char *p)
+{
+ return (hexval(p[0]) << 4) + hexval(p[1]);
+}
+
+uint32_t parse_argb(char **p)
+{
+ char *sp = *p;
+ char *ep;
+ uint32_t argb;
+ size_t len, dl;
+
+ if (*sp == '#')
+ sp++;
+
+ ep = sp;
+
+ while (my_isxdigit(*ep))
+ ep++;
+
+ *p = ep;
+ len = ep - sp;
+
+ switch (len) {
+ case 3: /* #rgb */
+ argb =
+ 0xff000000 +
+ (hexval(sp[0]) * 0x11 << 16) +
+ (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11);
+ break;
+ case 4: /* #argb */
+ argb =
+ (hexval(sp[0]) * 0x11 << 24) +
+ (hexval(sp[1]) * 0x11 << 16) +
+ (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11);
+ break;
+ case 6: /* #rrggbb */
+ case 9: /* #rrrgggbbb */
+ case 12: /* #rrrrggggbbbb */
+ dl = len / 3;
+ argb =
+ 0xff000000 +
+ (hexval2(sp + 0) << 16) +
+ (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2);
+ break;
+ case 8: /* #aarrggbb */
+ /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
+ assume the latter is a more common format */
+ case 16: /* #aaaarrrrggggbbbb */
+ dl = len / 4;
+ argb =
+ (hexval2(sp + 0) << 24) +
+ (hexval2(sp + dl) << 16) +
+ (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3);
+ break;
+ default:
+ argb = 0xffff0000; /* Bright red (error indication) */
+ break;
+ }
+
+ return argb;
+}
+
+/*
+ * Parser state. This is global so that including multiple
+ * files work as expected, which is that everything works the
+ * same way as if the files had been concatenated together.
+ */
+//static const char *append = NULL;
+extern const char *append;
+extern uint16_t PXERetry;
+static struct labeldata ld;
+
+static int parse_main_config(const char *filename);
+
+static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
+{
+ const char *const *p;
+ char *q;
+ enum kernel_type t = KT_NONE;
+
+ for (p = kernel_types; *p; p++, t++) {
+ if ((q = looking_at(cmdstr, *p))) {
+ *type = t;
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+static char *is_message_name(char *cmdstr, enum message_number *msgnr)
+{
+ char *q;
+ enum message_number i;
+
+ for (i = 0; i < MSG_COUNT; i++) {
+ if ((q = looking_at(cmdstr, messages[i].name))) {
+ *msgnr = i;
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+extern void get_msg_file(char *);
+
+void cat_help_file(int key)
+{
+ struct menu *cm = current_menu;
+ int fkey;
+
+ switch (key) {
+ case KEY_F1:
+ fkey = 0;
+ break;
+ case KEY_F2:
+ fkey = 1;
+ break;
+ case KEY_F3:
+ fkey = 2;
+ break;
+ case KEY_F4:
+ fkey = 3;
+ break;
+ case KEY_F5:
+ fkey = 4;
+ break;
+ case KEY_F6:
+ fkey = 5;
+ break;
+ case KEY_F7:
+ fkey = 6;
+ break;
+ case KEY_F8:
+ fkey = 7;
+ break;
+ case KEY_F9:
+ fkey = 8;
+ break;
+ case KEY_F10:
+ fkey = 9;
+ break;
+ case KEY_F11:
+ fkey = 10;
+ break;
+ case KEY_F12:
+ fkey = 11;
+ break;
+ default:
+ fkey = -1;
+ break;
+ }
+
+ if (fkey == -1)
+ return;
+
+ if (cm->fkeyhelp[fkey].textname) {
+ printf("\n");
+ get_msg_file((char *)cm->fkeyhelp[fkey].textname);
+ }
+}
+
+static char *is_fkey(char *cmdstr, int *fkeyno)
+{
+ char *q;
+ int no;
+
+ if ((cmdstr[0] | 0x20) != 'f')
+ return NULL;
+
+ no = strtoul(cmdstr + 1, &q, 10);
+ if (!my_isspace(*q))
+ return NULL;
+
+ if (no < 0 || no > 12)
+ return NULL;
+
+ *fkeyno = (no == 0) ? 10 : no - 1;
+ return q;
+}
+
+extern uint8_t FlowIgnore;
+extern uint8_t FlowInput;
+extern uint8_t FlowOutput;
+extern uint16_t SerialPort;
+extern uint16_t BaudDivisor;
+static uint8_t SerialNotice = 1;
+
+#define DEFAULT_BAUD 9600
+#define BAUD_DIVISOR 115200
+
+extern void sirq_cleanup_nowipe(void);
+extern void sirq_install(void);
+extern void write_serial_str(char *);
+
+extern void loadfont(char *);
+extern void loadkeys(char *);
+
+extern char syslinux_banner[];
+extern char copyright_str[];
+
+/*
+ * PATH-based lookup
+ *
+ * Each entry in the PATH directive is separated by a colon, e.g.
+ *
+ * PATH /bar:/bin/foo:/baz/bar/bin
+ */
+static int parse_path(char *p)
+{
+ struct path_entry *entry;
+ const char *str;
+
+ while (*p) {
+ char *c = p;
+
+ /* Find the next directory */
+ while (*c && *c != ':')
+ c++;
+
+ str = refstrndup(p, c - p);
+ if (!str)
+ goto bail;
+
+ entry = path_add(str);
+ refstr_put(str);
+
+ if (!entry)
+ goto bail;
+
+ if (!*c++)
+ break;
+ p = c;
+ }
+
+ return 0;
+
+bail:
+ return -1;
+}
+
+static void parse_config_file(FILE * f);
+
+static void do_include_menu(char *str, struct menu *m)
+{
+ const char *file;
+ char *p;
+ FILE *f;
+ int fd;
+
+ p = skipspace(str);
+ file = refdup_word(&p);
+ p = skipspace(p);
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ goto put;
+
+ f = fdopen(fd, "r");
+ if (!f)
+ goto bail;
+
+ if (*p) {
+ record(m, &ld, append);
+ m = current_menu = begin_submenu(p);
+ }
+
+ parse_config_file(f);
+
+ if (*p) {
+ record(m, &ld, append);
+ m = current_menu = end_submenu();
+ }
+
+bail:
+ close(fd);
+put:
+ refstr_put(file);
+
+}
+
+static void do_include(char *str)
+{
+ const char *file;
+ char *p;
+ FILE *f;
+ int fd;
+
+ p = skipspace(str);
+ file = refdup_word(&p);
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ goto put;
+
+ f = fdopen(fd, "r");
+ if (f)
+ parse_config_file(f);
+
+bail:
+ close(fd);
+put:
+ refstr_put(file);
+}
+
+static void parse_config_file(FILE * f)
+{
+ char line[MAX_LINE], *p, *ep, ch;
+ enum kernel_type type;
+ enum message_number msgnr;
+ int fkeyno;
+ struct menu *m = current_menu;
+
+ while (fgets(line, sizeof line, f)) {
+ p = strchr(line, '\r');
+ if (p)
+ *p = '\0';
+ p = strchr(line, '\n');
+ if (p)
+ *p = '\0';
+
+ p = skipspace(line);
+
+ if (looking_at(p, "menu")) {
+
+ p = skipspace(p + 4);
+
+ if (looking_at(p, "label")) {
+ if (ld.label) {
+ refstr_put(ld.menulabel);
+ ld.menulabel = refstrdup(skipspace(p + 5));
+ } else if (m->parent_entry) {
+ refstr_put(m->parent_entry->displayname);
+ m->parent_entry->displayname = refstrdup(skipspace(p + 5));
+ consider_for_hotkey(m->parent, m->parent_entry);
+ if (!m->title[0]) {
+ /* MENU LABEL -> MENU TITLE on submenu */
+ refstr_put(m->title);
+ m->title = strip_caret(m->parent_entry->displayname);
+ }
+ }
+ } else if (looking_at(p, "title")) {
+ refstr_put(m->title);
+ m->title = refstrdup(skipspace(p + 5));
+ if (m->parent_entry) {
+ /* MENU TITLE -> MENU LABEL on submenu */
+ if (m->parent_entry->displayname == m->label) {
+ refstr_put(m->parent_entry->displayname);
+ m->parent_entry->displayname = refstr_get(m->title);
+ }
+ }
+ } else if (looking_at(p, "default")) {
+ if (ld.label) {
+ ld.menudefault = 1;
+ } else if (m->parent_entry) {
+ m->parent->defentry = m->parent_entry->entry;
+ }
+ } else if (looking_at(p, "hide")) {
+ ld.menuhide = 1;
+ } else if (looking_at(p, "passwd")) {
+ if (ld.label) {
+ refstr_put(ld.passwd);
+ ld.passwd = refstrdup(skipspace(p + 6));
+ } else if (m->parent_entry) {
+ refstr_put(m->parent_entry->passwd);
+ m->parent_entry->passwd = refstrdup(skipspace(p + 6));
+ }
+ } else if (looking_at(p, "shiftkey")) {
+ shiftkey = 1;
+ } else if (looking_at(p, "save")) {
+ menusave = true;
+ if (ld.label)
+ ld.save = 1;
+ else
+ m->save = true;
+ } else if (looking_at(p, "nosave")) {
+ if (ld.label)
+ ld.save = -1;
+ else
+ m->save = false;
+ } else if (looking_at(p, "onerror")) {
+ refstr_put(m->onerror);
+ m->onerror = refstrdup(skipspace(p + 7));
+ onerrorlen = strlen(m->onerror);
+ refstr_put(onerror);
+ onerror = refstrdup(m->onerror);
+ } else if (looking_at(p, "master")) {
+ p = skipspace(p + 6);
+ if (looking_at(p, "passwd")) {
+ refstr_put(m->menu_master_passwd);
+ m->menu_master_passwd = refstrdup(skipspace(p + 6));
+ }
+ } else if ((ep = looking_at(p, "include"))) {
+ do_include_menu(ep, m);
+ } else if ((ep = looking_at(p, "background"))) {
+ p = skipspace(ep);
+ refstr_put(m->menu_background);
+ m->menu_background = refdup_word(&p);
+ } else if ((ep = looking_at(p, "hidden"))) {
+ hiddenmenu = 1;
+ } else if ((ep = is_message_name(p, &msgnr))) {
+ refstr_put(m->messages[msgnr]);
+ m->messages[msgnr] = refstrdup(skipspace(ep));
+ } else if ((ep = looking_at(p, "color")) ||
+ (ep = looking_at(p, "colour"))) {
+ int i;
+ struct color_table *cptr;
+ p = skipspace(ep);
+ cptr = m->color_table;
+ for (i = 0; i < menu_color_table_size; i++) {
+ if ((ep = looking_at(p, cptr->name))) {
+ p = skipspace(ep);
+ if (*p) {
+ if (looking_at(p, "*")) {
+ p++;
+ } else {
+ refstr_put(cptr->ansi);
+ cptr->ansi = refdup_word(&p);
+ }
+
+ p = skipspace(p);
+ if (*p) {
+ if (looking_at(p, "*"))
+ p++;
+ else
+ cptr->argb_fg = parse_argb(&p);
+
+ p = skipspace(p);
+ if (*p) {
+ if (looking_at(p, "*"))
+ p++;
+ else
+ cptr->argb_bg = parse_argb(&p);
+
+ /* Parse a shadow mode */
+ p = skipspace(p);
+ ch = *p | 0x20;
+ if (ch == 'n') /* none */
+ cptr->shadow = SHADOW_NONE;
+ else if (ch == 's') /* std, standard */
+ cptr->shadow = SHADOW_NORMAL;
+ else if (ch == 'a') /* all */
+ cptr->shadow = SHADOW_ALL;
+ else if (ch == 'r') /* rev, reverse */
+ cptr->shadow = SHADOW_REVERSE;
+ }
+ }
+ }
+ break;
+ }
+ cptr++;
+ }
+ } else if ((ep = looking_at(p, "msgcolor")) ||
+ (ep = looking_at(p, "msgcolour"))) {
+ unsigned int fg_mask = MSG_COLORS_DEF_FG;
+ unsigned int bg_mask = MSG_COLORS_DEF_BG;
+ enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
+
+ p = skipspace(ep);
+ if (*p) {
+ if (!looking_at(p, "*"))
+ fg_mask = parse_argb(&p);
+
+ p = skipspace(p);
+ if (*p) {
+ if (!looking_at(p, "*"))
+ bg_mask = parse_argb(&p);
+
+ p = skipspace(p);
+ switch (*p | 0x20) {
+ case 'n':
+ shadow = SHADOW_NONE;
+ break;
+ case 's':
+ shadow = SHADOW_NORMAL;
+ break;
+ case 'a':
+ shadow = SHADOW_ALL;
+ break;
+ case 'r':
+ shadow = SHADOW_REVERSE;
+ break;
+ default:
+ /* go with default */
+ break;
+ }
+ }
+ }
+ set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
+ } else if (looking_at(p, "separator")) {
+ record(m, &ld, append);
+ ld.label = refstr_get(empty_string);
+ ld.menuseparator = 1;
+ record(m, &ld, append);
+ } else if (looking_at(p, "disable") || looking_at(p, "disabled")) {
+ ld.menudisabled = 1;
+ } else if (looking_at(p, "indent")) {
+ ld.menuindent = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "begin")) {
+ record(m, &ld, append);
+ m = current_menu = begin_submenu(skipspace(p + 5));
+ } else if (looking_at(p, "end")) {
+ record(m, &ld, append);
+ m = current_menu = end_submenu();
+ } else if (looking_at(p, "quit")) {
+ if (ld.label)
+ ld.action = MA_QUIT;
+ } else if (looking_at(p, "goto")) {
+ if (ld.label) {
+ ld.action = MA_GOTO_UNRES;
+ refstr_put(ld.kernel);
+ ld.kernel = refstrdup(skipspace(p + 4));
+ }
+ } else if (looking_at(p, "exit")) {
+ p = skipspace(p + 4);
+ if (ld.label && m->parent) {
+ if (*p) {
+ /* This is really just a goto, except for the marker */
+ ld.action = MA_EXIT_UNRES;
+ refstr_put(ld.kernel);
+ ld.kernel = refstrdup(p);
+ } else {
+ ld.action = MA_EXIT;
+ ld.submenu = m->parent;
+ }
+ }
+ } else if (looking_at(p, "start")) {
+ start_menu = m;
+ } else {
+ /* Unknown, check for layout parameters */
+ enum parameter_number mp;
+ for (mp = 0; mp < NPARAMS; mp++) {
+ if ((ep = looking_at(p, mparm[mp].name))) {
+ m->mparm[mp] = atoi(skipspace(ep));
+ break;
+ }
+ }
+ }
+ }
+ /* feng: menu handling end */
+ else if (looking_at(p, "text")) {
+
+ /* loop till we fined the "endtext" */
+ enum text_cmd {
+ TEXT_UNKNOWN,
+ TEXT_HELP
+ } cmd = TEXT_UNKNOWN;
+ int len = ld.helptext ? strlen(ld.helptext) : 0;
+ int xlen;
+
+ p = skipspace(p + 4);
+
+ if (looking_at(p, "help"))
+ cmd = TEXT_HELP;
+
+ while (fgets(line, sizeof line, f)) {
+ p = skipspace(line);
+ if (looking_at(p, "endtext"))
+ break;
+
+ xlen = strlen(line);
+
+ switch (cmd) {
+ case TEXT_UNKNOWN:
+ break;
+ case TEXT_HELP:
+ ld.helptext = realloc(ld.helptext, len + xlen + 1);
+ memcpy(ld.helptext + len, line, xlen + 1);
+ len += xlen;
+ break;
+ }
+ }
+ } else if ((ep = is_fkey(p, &fkeyno))) {
+ p = skipspace(ep);
+ if (m->fkeyhelp[fkeyno].textname) {
+ refstr_put(m->fkeyhelp[fkeyno].textname);
+ m->fkeyhelp[fkeyno].textname = NULL;
+ }
+ if (m->fkeyhelp[fkeyno].background) {
+ refstr_put(m->fkeyhelp[fkeyno].background);
+ m->fkeyhelp[fkeyno].background = NULL;
+ }
+
+ refstr_put(m->fkeyhelp[fkeyno].textname);
+ m->fkeyhelp[fkeyno].textname = refdup_word(&p);
+ if (*p) {
+ p = skipspace(p);
+ m->fkeyhelp[fkeyno].background = refdup_word(&p);
+ }
+ } else if ((ep = looking_at(p, "include"))) {
+ do_include(ep);
+ } else if (looking_at(p, "append")) {
+ const char *a = refstrdup(skipspace(p + 6));
+ if (ld.label) {
+ refstr_put(ld.append);
+ ld.append = a;
+ } else {
+ refstr_put(append);
+ append = a;
+ }
+ //dprintf("we got a append: %s", a);
+ } else if (looking_at(p, "initrd")) {
+ const char *a = refstrdup(skipspace(p + 6));
+ if (ld.label) {
+ refstr_put(ld.initrd);
+ ld.initrd = a;
+ } else {
+ /* Ignore */
+ }
+ } else if (looking_at(p, "label")) {
+ p = skipspace(p + 5);
+ /* when first time see "label", it will not really record anything */
+ record(m, &ld, append);
+ ld.label = __refdup_word(p, NULL);
+ ld.kernel = __refdup_word(p, NULL);
+ /* feng: this is the default type for all */
+ ld.type = KT_KERNEL;
+ ld.passwd = NULL;
+ ld.append = NULL;
+ ld.initrd = NULL;
+ ld.menulabel = NULL;
+ ld.helptext = NULL;
+ ld.ipappend = SysAppends;
+ ld.menudefault = ld.menuhide = ld.menuseparator =
+ ld.menudisabled = ld.menuindent = 0;
+ } else if ((ep = is_kernel_type(p, &type))) {
+ if (ld.label) {
+ refstr_put(ld.kernel);
+ ld.kernel = refstrdup(skipspace(ep));
+ ld.type = type;
+ //dprintf("got a kernel: %s, type = %d", ld.kernel, ld.type);
+ }
+ } else if (looking_at(p, "timeout")) {
+ kbdtimeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10;
+ } else if (looking_at(p, "totaltimeout")) {
+ totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10;
+ } else if (looking_at(p, "ontimeout")) {
+ ontimeout = refstrdup(skipspace(p + 9));
+ ontimeoutlen = strlen(ontimeout);
+ } else if (looking_at(p, "allowoptions")) {
+ allowoptions = !!atoi(skipspace(p + 12));
+ } else if ((ep = looking_at(p, "ipappend")) ||
+ (ep = looking_at(p, "sysappend"))) {
+ uint32_t s = strtoul(skipspace(ep), NULL, 0);
+ if (ld.label)
+ ld.ipappend = s;
+ else
+ SysAppends = s;
+ } else if (looking_at(p, "default")) {
+ /* default could be a kernel image or another label */
+ refstr_put(globaldefault);
+ globaldefault = refstrdup(skipspace(p + 7));
+
+ /*
+ * On the chance that "default" is actually a kernel image
+ * and not a label, store a copy of it, but only if we
+ * haven't seen a "ui" command. "ui" commands take
+ * precendence over "default" commands.
+ */
+ if (defaultlevel < LEVEL_UI) {
+ defaultlevel = LEVEL_DEFAULT;
+ refstr_put(default_cmd);
+ default_cmd = refstrdup(globaldefault);
+ }
+ } else if (looking_at(p, "ui")) {
+ has_ui = 1;
+ defaultlevel = LEVEL_UI;
+ refstr_put(default_cmd);
+ default_cmd = refstrdup(skipspace(p + 2));
+ }
+
+ /*
+ * subset 1: pc_opencmd
+ * display/font/kbdmap are rather similar, open a file then do sth
+ */
+ else if (looking_at(p, "display")) {
+ const char *filename;
+ char *dst = KernelName;
+ size_t len = FILENAME_MAX - 1;
+
+ filename = refstrdup(skipspace(p + 7));
+
+ while (len-- && not_whitespace(*filename))
+ *dst++ = *filename++;
+ *dst = '\0';
+
+ get_msg_file(KernelName);
+ refstr_put(filename);
+ } else if (looking_at(p, "font")) {
+ const char *filename;
+ char *dst = KernelName;
+ size_t len = FILENAME_MAX - 1;
+
+ filename = refstrdup(skipspace(p + 4));
+
+ while (len-- && not_whitespace(*filename))
+ *dst++ = *filename++;
+ *dst = '\0';
+
+ loadfont(KernelName);
+ refstr_put(filename);
+ } else if (looking_at(p, "kbdmap")) {
+ const char *filename;
+
+ filename = refstrdup(skipspace(p + 6));
+ loadkeys(filename);
+ refstr_put(filename);
+ }
+ /*
+ * subset 2: pc_setint16
+ * set a global flag
+ */
+ else if (looking_at(p, "implicit")) {
+ allowimplicit = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "prompt")) {
+ forceprompt = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "console")) {
+ DisplayCon = atoi(skipspace(p + 7));
+ } else if (looking_at(p, "allowoptions")) {
+ allowoptions = atoi(skipspace(p + 12));
+ } else if (looking_at(p, "noescape")) {
+ noescape = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "nocomplete")) {
+ nocomplete = atoi(skipspace(p + 10));
+ } else if (looking_at(p, "nohalt")) {
+ NoHalt = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "onerror")) {
+ refstr_put(m->onerror);
+ m->onerror = refstrdup(skipspace(p + 7));
+ onerrorlen = strlen(m->onerror);
+ refstr_put(onerror);
+ onerror = refstrdup(m->onerror);
+ }
+
+ else if (looking_at(p, "pxeretry"))
+ PXERetry = atoi(skipspace(p + 8));
+
+ /* serial setting, bps, flow control */
+ else if (looking_at(p, "serial")) {
+ uint16_t port, flow;
+ uint32_t baud;
+
+ p = skipspace(p + 6);
+ port = atoi(p);
+
+ while (isalnum(*p))
+ p++;
+ p = skipspace(p);
+
+ /* Default to no flow control */
+ FlowOutput = 0;
+ FlowInput = 0;
+
+ baud = DEFAULT_BAUD;
+ if (isalnum(*p)) {
+ uint8_t ignore;
+
+ /* setup baud */
+ baud = atoi(p);
+ while (isalnum(*p))
+ p++;
+ p = skipspace(p);
+
+ ignore = 0;
+ flow = 0;
+ if (isalnum(*p)) {
+ /* flow control */
+ flow = atoi(p);
+ ignore = ((flow & 0x0F00) >> 4);
+ }
+
+ FlowIgnore = ignore;
+ flow = ((flow & 0xff) << 8) | (flow & 0xff);
+ flow &= 0xF00B;
+ FlowOutput = (flow & 0xff);
+ FlowInput = ((flow & 0xff00) >> 8);
+ }
+
+ /*
+ * Parse baud
+ */
+ if (baud < 75) {
+ /* < 75 baud == bogus */
+ SerialPort = 0;
+ continue;
+ }
+
+ baud = BAUD_DIVISOR / baud;
+ baud &= 0xffff;
+ BaudDivisor = baud;
+
+ port = get_serial_port(port);
+ SerialPort = port;
+
+ /*
+ * Begin code to actually set up the serial port
+ */
+ sirq_cleanup_nowipe();
+
+ outb(0x83, port + 3); /* Enable DLAB */
+ io_delay();
+
+ outb((baud & 0xff), port); /* write divisor to LS */
+ io_delay();
+
+ outb(((baud & 0xff00) >> 8), port + 1); /* write to MS */
+ io_delay();
+
+ outb(0x03, port + 3); /* Disable DLAB */
+ io_delay();
+
+ /*
+ * Read back LCR (detect missing hw). If nothing here
+ * we'll read 00 or FF.
+ */
+ if (inb(port + 3) != 0x03) {
+ /* Assume serial port busted */
+ SerialPort = 0;
+ continue;
+ }
+
+ outb(0x01, port + 2); /* Enable FIFOs if present */
+ io_delay();
+
+ /* Disable FIFO if unusable */
+ if (inb(port + 2) < 0x0C0) {
+ outb(0, port + 2);
+ io_delay();
+ }
+
+ /* Assert bits in MCR */
+ outb(FlowOutput, port + 4);
+ io_delay();
+
+ /* Enable interrupts if requested */
+ if (FlowOutput & 0x8)
+ sirq_install();
+
+ /* Show some life */
+ if (SerialNotice != 0) {
+ SerialNotice = 0;
+
+ write_serial_str(syslinux_banner);
+ write_serial_str(copyright_str);
+ }
+
+ } else if (looking_at(p, "say")) {
+ printf("%s\n", p+4);
+ } else if (looking_at(p, "path")) {
+ if (parse_path(skipspace(p + 4)))
+ printf("Failed to parse PATH\n");
+ } else if (looking_at(p, "sendcookies")) {
+ const union syslinux_derivative_info *sdi;
+
+ p += strlen("sendcookies");
+ sdi = syslinux_derivative_info();
+
+ if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
+ SendCookies = strtoul(skipspace(p), NULL, 10);
+ http_bake_cookies();
+ }
+ }
+ }
+}
+
+static int parse_main_config(const char *filename)
+{
+ const char *mode = "r";
+ FILE *f;
+ int fd;
+
+ if (!filename)
+ fd = open_config();
+ else
+ fd = open(filename, O_RDONLY);
+
+ if (fd < 0)
+ return fd;
+
+ if (config_cwd[0]) {
+ if (chdir(config_cwd) < 0)
+ printf("Failed to chdir to %s\n", config_cwd);
+ config_cwd[0] = '\0';
+ }
+
+ f = fdopen(fd, mode);
+ parse_config_file(f);
+
+ /*
+ * Update ConfigName so that syslinux_config_file() returns
+ * the filename we just opened. filesystem-specific
+ * open_config() implementations are expected to update
+ * ConfigName themselves.
+ */
+ if (filename)
+ strcpy(ConfigName, filename);
+
+ return 0;
+}
+
+static void resolve_gotos(void)
+{
+ struct menu_entry *me;
+ struct menu *m;
+
+ for (me = all_entries; me; me = me->next) {
+ if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) {
+ m = find_menu(me->cmdline);
+ refstr_put(me->cmdline);
+ me->cmdline = NULL;
+ if (m) {
+ me->submenu = m;
+ me->action--; /* Drop the _UNRES */
+ } else {
+ me->action = MA_DISABLED;
+ }
+ }
+ }
+}
+
+void parse_configs(char **argv)
+{
+ const char *filename;
+ struct menu *m;
+ struct menu_entry *me;
+ dprintf("enter");
+
+ empty_string = refstrdup("");
+
+ /* feng: reset current menu_list and entry list */
+ menu_list = NULL;
+ all_entries = NULL;
+
+ /* Initialize defaults for the root and hidden menus */
+ hide_menu = new_menu(NULL, NULL, refstrdup(".hidden"));
+ root_menu = new_menu(NULL, NULL, refstrdup(".top"));
+ start_menu = root_menu;
+
+ /* Other initialization */
+ memset(&ld, 0, sizeof(struct labeldata));
+
+ /* Actually process the files */
+ current_menu = root_menu;
+
+ if (!argv || !*argv) {
+ if (parse_main_config(NULL) < 0) {
+ printf("WARNING: No configuration file found\n");
+ return;
+ }
+ } else {
+ while ((filename = *argv++)) {
+ dprintf("Parsing config: %s", filename);
+ parse_main_config(filename);
+ }
+ }
+
+ /* On final EOF process the last label statement */
+ record(current_menu, &ld, append);
+
+ /* Common postprocessing */
+ resolve_gotos();
+
+ /* Handle global default */
+ //if (has_ui && globaldefault) {
+ if (globaldefault) {
+ dprintf("gloabldefault = %s", globaldefault);
+ me = find_label(globaldefault);
+ if (me && me->menu != hide_menu) {
+ me->menu->defentry = me->entry;
+ start_menu = me->menu;
+ default_menu = me->menu;
+ }
+ }
+
+ /* If "menu save" is active, let the ADV override the global default */
+ if (menusave) {
+ size_t len;
+ const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len);
+ char *lstr;
+ if (lbl && len) {
+ lstr = refstr_alloc(len);
+ memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */
+ me = find_label(lstr);
+ if (me && me->menu != hide_menu) {
+ me->menu->defentry = me->entry;
+ start_menu = me->menu;
+ }
+ refstr_put(lstr);
+ }
+ }
+
+ /* Final per-menu initialization, with all labels known */
+ for (m = menu_list; m; m = m->next) {
+ m->curentry = m->defentry; /* All menus start at their defaults */
+
+ if (m->ontimeout)
+ m->ontimeout = unlabel(m->ontimeout);
+ if (m->onerror)
+ m->onerror = unlabel(m->onerror);
+ }
+}
diff --git a/com32/elflink/ldlinux/refstr.c b/com32/elflink/ldlinux/refstr.c
new file mode 100644
index 00000000..f9d98e11
--- /dev/null
+++ b/com32/elflink/ldlinux/refstr.c
@@ -0,0 +1,106 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * refstr.c
+ *
+ * Simple reference-counted strings
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/module.h>
+#include "refstr.h"
+
+/* Allocate space for a refstring of len bytes, plus final null */
+/* The final null is inserted in the string; the rest is uninitialized. */
+char *refstr_alloc(size_t len)
+{
+ char *r = malloc(sizeof(unsigned int) + len + 1);
+ if (!r)
+ return NULL;
+ *(unsigned int *)r = 1;
+ r += sizeof(unsigned int);
+ r[len] = '\0';
+ return r;
+}
+
+const char *refstrndup(const char *str, size_t len)
+{
+ char *r;
+
+ if (!str)
+ return NULL;
+
+ len = strnlen(str, len);
+ r = refstr_alloc(len);
+ if (r)
+ memcpy(r, str, len);
+ return r;
+}
+
+const char *refstrdup(const char *str)
+{
+ char *r;
+ size_t len;
+
+ if (!str)
+ return NULL;
+
+ len = strlen(str);
+ r = refstr_alloc(len);
+ if (r)
+ memcpy(r, str, len);
+ return r;
+}
+
+int vrsprintf(const char **bufp, const char *fmt, va_list ap)
+{
+ va_list ap1;
+ int len;
+ char *p;
+
+ va_copy(ap1, ap);
+ len = vsnprintf(NULL, 0, fmt, ap1);
+ va_end(ap1);
+
+ *bufp = p = refstr_alloc(len);
+ if (!p)
+ return -1;
+
+ return vsnprintf(p, len + 1, fmt, ap);
+}
+
+int rsprintf(const char **bufp, const char *fmt, ...)
+{
+ int rv;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rv = vrsprintf(bufp, fmt, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+void refstr_put(const char *r)
+{
+ unsigned int *ref;
+
+ if (r) {
+ ref = (unsigned int *)r - 1;
+
+ if (!--*ref)
+ free(ref);
+ }
+}
diff --git a/com32/lib/syslinux/setadv.c b/com32/elflink/ldlinux/setadv.c
index 40f00a4e..2e386213 100644
--- a/com32/lib/syslinux/setadv.c
+++ b/com32/elflink/ldlinux/setadv.c
@@ -45,7 +45,7 @@
#include <errno.h>
#include <alloca.h>
-int syslinux_setadv(int tag, size_t size, const void *data)
+__export int syslinux_setadv(int tag, size_t size, const void *data)
{
uint8_t *p, *advtmp;
size_t rleft, left;
diff --git a/com32/elflink/test_com32.c b/com32/elflink/test_com32.c
new file mode 100644
index 00000000..19089dbd
--- /dev/null
+++ b/com32/elflink/test_com32.c
@@ -0,0 +1,208 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <console.h>
+#include <string.h>
+
+#include <sys/module.h>
+#include <sys/exec.h>
+
+#define INFO_PRINT(fmt, args...) printf("[COM32] " fmt, ##args)
+
+#define MAX_COMMAND_SIZE 80 // Maximum size of the cmd line
+#define COMMAND_DELIM " \t\n" // Whitespace delimiters
+#define MAX_COMMAND_ARGS (MAX_COMMAND_SIZE/2) // Maximum argument count for
+ // program execution
+
+/**
+ * print_help - Display usage instructions on the screen.
+ */
+static void print_help(void)
+{
+ printf("List of available commands:\n");
+ printf("exit - exits the program\n");
+ printf("help - shows this message\n");
+ printf("load <library>... - loads the libraries into the environment\n");
+ printf("spawn <executable> <args> - launches an executable module\n");
+ printf
+ ("unload <library>... - unloads the libraries from the environment\n");
+ printf("list - prints the currently loaded modules\n");
+}
+
+/**
+ * print_prompt - Display the command prompt.
+ */
+static void print_prompt(void)
+{
+ printf("\nelflink> ");
+}
+
+/**
+ * read_command - Read a new command from the standard input.
+ * @cmd: the buffer to store the command
+ * @size: the maximum size of the string that can be stored in the buffer
+ *
+ * If the command is larger than the specified size, it is truncated.
+ */
+static void read_command(char *cmd, int size)
+{
+ char *nl = NULL;
+ fgets(cmd, size, stdin);
+
+ // Strip the newline
+ nl = strchr(cmd, '\n');
+
+ if (nl != NULL)
+ *nl = '\0';
+}
+
+/**
+ * process_spawn - Handles the execution of a 'spawn' command.
+ *
+ * The command line is in the internal buffer of strtok.
+ */
+static void process_spawn(void)
+{
+ // Compose the command line
+ char **cmd_line = malloc((MAX_COMMAND_ARGS + 1) * sizeof(char *));
+ int argc = 0, result;
+ char *crt_arg;
+
+ do {
+ crt_arg = strtok(NULL, COMMAND_DELIM);
+ if (crt_arg != NULL && strlen(crt_arg) > 0) {
+ cmd_line[argc] = crt_arg;
+ argc++;
+ } else {
+ break;
+ }
+ } while (argc < MAX_COMMAND_ARGS);
+
+ cmd_line[argc] = NULL;
+
+ if (cmd_line[0] == NULL) {
+ printf("You must specify an executable module.\n");
+ } else {
+ result = spawnv(cmd_line[0], cmd_line);
+
+ printf("Spawn returned %d\n", result);
+ }
+
+ free(cmd_line);
+}
+
+/**
+ * process_library - Handles the execution of the 'load' and 'unload' commands.
+ * @load: contains 1 if the libraries are to be loaded, 0 for unloading.
+ *
+ * The command line is in the internal buffer of strtok.
+ */
+static void process_library(int load)
+{
+ char *crt_lib;
+ int result;
+
+ while ((crt_lib = strtok(NULL, COMMAND_DELIM)) != NULL) {
+ if (strlen(crt_lib) > 0) {
+ if (load)
+ result = load_library(crt_lib);
+ else
+ result = unload_library(crt_lib);
+
+ if (result == 0) {
+ printf("Library '%s' %sloaded successfully.\n", crt_lib,
+ load ? "" : "un");
+ } else {
+ printf("Could not %sload library '%s': error %d\n",
+ load ? "" : "un", crt_lib, result);
+ }
+ }
+ }
+}
+
+/**
+ * process_list - Handles the execution of the 'list' command.
+ *
+ */
+static void process_list(void)
+{
+ struct elf_module *module;
+ struct module_dep *crt_dep;
+
+ for_each_module(module) {
+ printf("%s (%dK, %s, %s) : ", module->name, module->module_size >> 10,
+ module->shallow ? "shallow" : "regular",
+ module->main_func == NULL ? "library" : "program");
+
+ list_for_each_entry(crt_dep, &module->required, list) {
+ printf("%s ", crt_dep->module->name);
+ }
+
+ printf("\n");
+ }
+}
+
+/**
+ * process_command - Recognizes the requested command and executes it.
+ * @cmd: the command to be executed.
+ *
+ * Returns 1 if the command was 'exit', 0 otherwise.
+ */
+static int process_command(char *cmd)
+{
+ char *cmd_name;
+
+ cmd_name = strtok(cmd, COMMAND_DELIM);
+
+ if (strcmp(cmd_name, "exit") == 0) {
+ printf("Goodbye!\n");
+ return 1;
+ } else if (strcmp(cmd_name, "help") == 0) {
+ print_help();
+ } else if (strcmp(cmd_name, "load") == 0) {
+ process_library(1);
+ } else if (strcmp(cmd_name, "spawn") == 0) {
+ process_spawn();
+ } else if (strcmp(cmd_name, "unload") == 0) {
+ process_library(0);
+ } else if (strcmp(cmd_name, "list") == 0) {
+ process_list();
+ } else {
+ printf("Unknown command. Type 'help' for a list of valid commands.\n");
+ }
+
+ return 0;
+}
+
+/**
+ * The entry point of 'test_com32' COM module.
+ */
+int main(int argc, char **argv)
+{
+ int done = 0;
+ int res;
+ char command[MAX_COMMAND_SIZE] = { 0 };
+
+ // Open a standard r/w console
+ openconsole(&dev_stdcon_r, &dev_stdcon_w);
+
+ res = exec_init();
+ if (res != 0) {
+ printf("Failed to initialize the execution environment.\n");
+ return res;
+ } else {
+ printf("Execution environment initialized successfully.\n");
+ }
+
+ printf("\nFor a list of available commands, type 'help'.\n");
+
+ do {
+ print_prompt();
+ read_command(command, MAX_COMMAND_SIZE);
+ done = process_command(command);
+
+ } while (!done);
+
+ exec_term();
+
+ return 0;
+}
diff --git a/com32/gfxboot/Makefile b/com32/gfxboot/Makefile
index 183115f4..bd0bab11 100644
--- a/com32/gfxboot/Makefile
+++ b/com32/gfxboot/Makefile
@@ -13,7 +13,7 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
MODULES = gfxboot.c32
@@ -31,7 +31,7 @@ realmode_callback.o: realmode_callback.asm
$*.tmp $@
tidy dist:
- rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
+ rm -f *.o *.lo *.a *.lst .*.d *.tmp
clean: tidy
rm -f *.lnx
diff --git a/com32/gfxboot/gfxboot.c b/com32/gfxboot/gfxboot.c
index aa05caf8..9c07d263 100644
--- a/com32/gfxboot/gfxboot.c
+++ b/com32/gfxboot/gfxboot.c
@@ -393,7 +393,7 @@ int read_config_file(const char *filename)
continue;
}
- if(!strcasecmp(s, "ipappend")) {
+ if(!strcasecmp(s, "ipappend") || !strcasecmp(s, "sysappend")) {
(menu_ptr ?: menu_default)->ipappend = strdup(t);
continue;
}
diff --git a/com32/gpllib/Makefile b/com32/gpllib/Makefile
index 4b7b8468..3ccc0dc6 100644
--- a/com32/gpllib/Makefile
+++ b/com32/gpllib/Makefile
@@ -19,26 +19,24 @@ AUXDIR = $(DATADIR)/syslinux
INCDIR = /usr/include
COM32DIR = $(AUXDIR)/com32
-all: libcom32gpl.a
+all: libgpl.c32
-libcom32gpl.a : $(LIBOBJS)
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
+libgpl.elf : $(LIBOBJS)
+ $(LD) -shared $(LDFLAGS) -soname $(patsubst %.elf,%.c32,$(@F)) -o $@ $^
tidy dist clean:
- find . \( -name \*.o -o -name \*.a -o -name .\*.d -o -name \*.tmp \) -print0 | \
+ find . \( -name \*.o -o -name .\*.d -o -name \*.tmp \) -print0 | \
xargs -0r rm -f
spotless: clean
- rm -f *.a
+ rm -f *.c32
rm -f *~ \#* */*~ */\#*
# Mixing in the GPL include files is suboptimal, but I'm not sure
# there is a better way to do it.
install: all
mkdir -m 755 -p $(INSTALLROOT)$(COM32DIR)
- install -m 644 libcom32gpl.a $(INSTALLROOT)$(COM32DIR)
+ install -m 644 libgpl.c32 $(INSTALLROOT)$(COM32DIR)
mkdir -p $(INSTALLROOT)$(COM32DIR)/include/
cp -r ../gplinclude $(INSTALLROOT)$(COM32DIR)/include/
diff --git a/com32/gpllib/disk/geom.c b/com32/gpllib/disk/geom.c
index 9e673ed4..e1095200 100644
--- a/com32/gpllib/disk/geom.c
+++ b/com32/gpllib/disk/geom.c
@@ -120,7 +120,7 @@ static int detect_extensions(struct driveinfo *drive_info)
static int get_drive_parameters_with_extensions(struct driveinfo *drive_info)
{
com32sys_t inreg, outreg;
- struct edd_device_parameters *dp = __com32.cs_bounce;
+ struct edd_device_parameters *dp;
memset(&inreg, 0, sizeof inreg);
@@ -134,6 +134,10 @@ static int get_drive_parameters_with_extensions(struct driveinfo *drive_info)
* If the buffer length is less than 26 on entry an error shall be
* returned.
*/
+ dp = lmalloc(sizeof *dp);
+ if (!dp)
+ return -1;
+
dp->len = sizeof(struct edd_device_parameters);
inreg.esi.w[0] = OFFS(dp);
@@ -144,10 +148,13 @@ static int get_drive_parameters_with_extensions(struct driveinfo *drive_info)
__intcall(0x13, &inreg, &outreg);
/* CF set on error */
- if (outreg.eflags.l & EFLAGS_CF)
+ if (outreg.eflags.l & EFLAGS_CF) {
+ lfree(dp);
return outreg.eax.b[1];
+ }
memcpy(&drive_info->edd_params, dp, sizeof drive_info->edd_params);
+ lfree(dp);
return 0;
}
diff --git a/com32/gpllib/disk/read.c b/com32/gpllib/disk/read.c
index 7a6cc430..541957f9 100644
--- a/com32/gpllib/disk/read.c
+++ b/com32/gpllib/disk/read.c
@@ -76,13 +76,22 @@ int read_sectors(struct driveinfo *drive_info, void *data,
const unsigned int lba, const int sectors)
{
com32sys_t inreg, outreg;
- struct ebios_dapa *dapa = __com32.cs_bounce;
- void *buf = (char *)__com32.cs_bounce + sectors * SECTOR;
+ struct ebios_dapa *dapa;
+ void *buf;
char *bufp = data;
+ int rv = -1;
if (get_drive_parameters(drive_info) == -1)
return -1;
+ buf = lmalloc(sectors * SECTOR);
+ if (!buf)
+ return -1;
+
+ dapa = lmalloc(sizeof(*dapa));
+ if (!dapa)
+ goto fail;
+
memset(&inreg, 0, sizeof inreg);
if (drive_info->ebios) {
@@ -102,7 +111,7 @@ int read_sectors(struct driveinfo *drive_info, void *data,
if (!drive_info->cbios) { // XXX errno
/* We failed to get the geometry */
if (lba)
- return -1; /* Can only read MBR */
+ goto fail; /* Can only read MBR */
s = 1;
h = 0;
@@ -112,7 +121,7 @@ int read_sectors(struct driveinfo *drive_info, void *data,
// XXX errno
if (s > 63 || h > 256 || c > 1023)
- return -1;
+ goto fail;
inreg.eax.w[0] = 0x0201; /* Read one sector */
inreg.ecx.b[1] = c & 0xff;
@@ -126,10 +135,14 @@ int read_sectors(struct driveinfo *drive_info, void *data,
/* Perform the read */
if (int13_retry(&inreg, &outreg)) {
errno_disk = outreg.eax.b[1];
- return -1; /* Give up */
+ goto fail; /* Give up */
}
memcpy(bufp, buf, sectors * SECTOR);
+ rv = sectors;
- return sectors;
+fail:
+ lfree(dapa);
+ lfree(buf);
+ return rv;
}
diff --git a/com32/gpllib/disk/write.c b/com32/gpllib/disk/write.c
index 89e530d9..d183adef 100644
--- a/com32/gpllib/disk/write.c
+++ b/com32/gpllib/disk/write.c
@@ -36,8 +36,17 @@ int write_sectors(const struct driveinfo *drive_info, const unsigned int lba,
const void *data, const int size)
{
com32sys_t inreg, outreg;
- struct ebios_dapa *dapa = __com32.cs_bounce;
- void *buf = (char *)__com32.cs_bounce + size;
+ struct ebios_dapa *dapa;
+ void *buf;
+ int rv = -1;
+
+ buf = lmalloc(size);
+ if (!buf)
+ return -1;
+
+ dapa = lmalloc(sizeof(*dapa));
+ if (!dapa)
+ goto out;
memcpy(buf, data, size);
memset(&inreg, 0, sizeof inreg);
@@ -59,7 +68,7 @@ int write_sectors(const struct driveinfo *drive_info, const unsigned int lba,
if (!drive_info->cbios) { // XXX errno
/* We failed to get the geometry */
if (lba)
- return -1; /* Can only write MBR */
+ goto out; /* Can only write MBR */
s = 1;
h = 0;
@@ -69,7 +78,7 @@ int write_sectors(const struct driveinfo *drive_info, const unsigned int lba,
// XXX errno
if (s > 63 || h > 256 || c > 1023)
- return -1;
+ goto out;
inreg.eax.w[0] = 0x0301; /* Write one sector */
inreg.ecx.b[1] = c & 0xff;
@@ -82,10 +91,13 @@ int write_sectors(const struct driveinfo *drive_info, const unsigned int lba,
/* Perform the write */
if (int13_retry(&inreg, &outreg)) {
- errno_disk = outreg.eax.b[1];
- return -1; /* Give up */
+ errno_disk = outreg.eax.b[1]; /* Give up */
} else
- return size;
+ rv = size;
+out:
+ lfree(dapa);
+ lfree(buf);
+ return rv;
}
/**
diff --git a/com32/gpllib/memory.c b/com32/gpllib/memory.c
index 28a95ff4..06c746da 100644
--- a/com32/gpllib/memory.c
+++ b/com32/gpllib/memory.c
@@ -87,15 +87,20 @@ void detect_memory_e820(struct e820entry *desc, int size_map, int *size_found)
{
int count = 0;
static struct e820_ext_entry buf; /* static so it is zeroed */
+ void *bounce;
com32sys_t ireg, oreg;
memset(&ireg, 0, sizeof ireg);
+ bounce = lmalloc(sizeof buf);
+ if (!bounce)
+ goto out;
+
ireg.eax.w[0] = 0xe820;
ireg.edx.l = SMAP;
ireg.ecx.l = sizeof(struct e820_ext_entry);
- ireg.edi.w[0] = OFFS(__com32.cs_bounce);
- ireg.es = SEG(__com32.cs_bounce);
+ ireg.edi.w[0] = OFFS(bounce);
+ ireg.es = SEG(bounce);
/*
* Set this here so that if the BIOS doesn't change this field
@@ -105,7 +110,7 @@ void detect_memory_e820(struct e820entry *desc, int size_map, int *size_found)
buf.ext_flags = 1;
do {
- memcpy(__com32.cs_bounce, &buf, sizeof buf);
+ memcpy(bounce, &buf, sizeof buf);
/* Important: %edx and %esi are clobbered by some BIOSes,
so they must be either used for the error output
@@ -126,7 +131,7 @@ void detect_memory_e820(struct e820entry *desc, int size_map, int *size_found)
if (oreg.eflags.l & EFLAGS_CF || oreg.ecx.l < 20)
break;
- memcpy(&buf, __com32.cs_bounce, sizeof buf);
+ memcpy(&buf, bounce, sizeof buf);
/*
* ACPI 3.0 added the extended flags support. If bit 0
@@ -143,6 +148,8 @@ void detect_memory_e820(struct e820entry *desc, int size_map, int *size_found)
ireg.ebx.l = oreg.ebx.l;
} while (ireg.ebx.l && count < size_map);
+out:
+ lfree(bounce);
*size_found = count;
}
diff --git a/com32/hdt/Makefile b/com32/hdt/Makefile
index add640a7..42f5c0d1 100644
--- a/com32/hdt/Makefile
+++ b/com32/hdt/Makefile
@@ -17,9 +17,10 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
-LIBS = ../cmenu/libmenu/libmenu.a ../libupload/libcom32upload.a
+LIBS = ../libupload/libcom32upload.a
+C_LIBS += $(com32)/cmenu/libmenu/libmenu.c32
CFLAGS += -I$(com32)/cmenu/libmenu -I$(com32)
MODULES = hdt.c32
diff --git a/com32/hdt/hdt-cli.c b/com32/hdt/hdt-cli.c
index 7542da83..216b6bde 100644
--- a/com32/hdt/hdt-cli.c
+++ b/com32/hdt/hdt-cli.c
@@ -649,7 +649,7 @@ static void exec_command(char *line, struct s_hardware *hardware)
if ((current_module->nomodule == true) && ( module != NULL)) {
dprintf("CLI_DEBUG exec: Reworking arguments with argc=%d\n",argc);
char **new_argv=NULL;
- new_argv=malloc((argc + 2)*sizeof(char));
+ new_argv=malloc((argc + 2)*sizeof(char *));
for (int argc_iter=0; argc_iter<argc; argc_iter++) {
dprintf("CLI_DEBUG exec rework : copy %d to %d (%s)\n",argc_iter,argc_iter+1,argv[argc_iter]);
new_argv[argc_iter+1] = malloc(strlen(argv[argc_iter]));
diff --git a/com32/hdt/hdt-common.c b/com32/hdt/hdt-common.c
index dcad28cd..289d74e3 100644
--- a/com32/hdt/hdt-common.c
+++ b/com32/hdt/hdt-common.c
@@ -317,6 +317,7 @@ int detect_vesa(struct s_hardware *hardware)
struct vesa_mode_info *mi;
uint16_t mode, *mode_ptr;
char *oem_ptr;
+ int rv = -1;
if (hardware->vesa_detection == true)
return -1;
@@ -324,9 +325,13 @@ int detect_vesa(struct s_hardware *hardware)
hardware->vesa_detection = true;
hardware->is_vesa_valid = false;
- /* Allocate space in the bounce buffer for these structures */
- gi = &((struct vesa_info *)__com32.cs_bounce)->gi;
- mi = &((struct vesa_info *)__com32.cs_bounce)->mi;
+ gi = lmalloc(sizeof(*gi));
+ if (!gi)
+ return -1;
+
+ mi = lmalloc(sizeof(*mi));
+ if (!mi)
+ goto out;
gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */
rm.eax.w[0] = 0x4F00; /* Get SVGA general information */
@@ -335,7 +340,7 @@ int detect_vesa(struct s_hardware *hardware)
__intcall(0x10, &rm, &rm);
if (rm.eax.w[0] != 0x004F) {
- return -1;
+ goto out;
};
mode_ptr = GET_PTR(gi->video_mode_ptr);
@@ -374,7 +379,12 @@ int detect_vesa(struct s_hardware *hardware)
hardware->vesa.vmi_count++;
}
hardware->is_vesa_valid = true;
- return 0;
+
+ rv = 0;
+out:
+ lfree(mi);
+ lfree(gi);
+ return rv;
}
/* Try to detect disks from port 0x80 to 0xff */
diff --git a/com32/hdt/hdt-common.h b/com32/hdt/hdt-common.h
index c2299b48..53aa43e7 100644
--- a/com32/hdt/hdt-common.h
+++ b/com32/hdt/hdt-common.h
@@ -54,9 +54,6 @@
#include <acpi/acpi.h>
#include <libupload/upload_backend.h>
-/* Declare a variable or data structure as unused. */
-#define __unused __attribute__ (( unused ))
-
/* This two values are used for switching for the menu to the CLI mode */
#define HDT_SWITCH_TO_CLI "hdt_switch_to_cli"
#define HDT_DUMP "hdt_dump"
diff --git a/com32/hdt/hdt.c b/com32/hdt/hdt.c
index 653995d0..67b3ab0c 100644
--- a/com32/hdt/hdt.c
+++ b/com32/hdt/hdt.c
@@ -48,7 +48,7 @@ int max_console_lines = MAX_CLI_LINES;
int main(const int argc, const char *argv[])
{
char version_string[256];
- struct s_hardware hardware;
+ static struct s_hardware hardware;
snprintf(version_string, sizeof version_string, "%s %s (%s)",
PRODUCT_NAME, VERSION, CODENAME);
diff --git a/com32/include/byteswap.h b/com32/include/byteswap.h
new file mode 100644
index 00000000..11870e97
--- /dev/null
+++ b/com32/include/byteswap.h
@@ -0,0 +1,50 @@
+#ifndef _BYTESWAP_H
+#define _BYTESWAP_H
+
+/* COM32 will be running on an i386 platform */
+
+#include <stdint.h>
+#include <klibc/compiler.h>
+
+#define __bswap_16_macro(v) ((uint16_t) \
+ (((uint16_t)(v) << 8) | \
+ ((uint16_t)(v) >> 8)))
+
+static inline __constfunc uint16_t __bswap_16(uint16_t v)
+{
+ return __bswap_16_macro(v);
+}
+
+#define bswap_16(x) (__builtin_constant_p(x) ? \
+ __bswap_16_macro(x) : __bswap_16(x))
+
+#define __bswap_32_macro(v) ((uint32_t) \
+ ((((uint32_t)(v) & 0x000000ff) << 24) | \
+ (((uint32_t)(v) & 0x0000ff00) << 8) | \
+ (((uint32_t)(v) & 0x00ff0000) >> 8) | \
+ (((uint32_t)(v) & 0xff000000) >> 24)))
+
+static inline __constfunc uint32_t __bswap_32(uint32_t v)
+{
+ asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0"
+ : "+q" (v));
+ return v;
+}
+
+#define bswap_32(x) (__builtin_constant_p(x) ? \
+ __bswap_32_macro(x) : __bswap_32(x))
+
+
+#define __bswap_64_macro(v) ((uint64_t) \
+ (((uint64_t)__bswap_32_macro((uint32_t)(v)) << 32) | \
+ (__bswap_32__macro((uint32_t)((uint64_t)(v) >> 32)))))
+
+static inline __constfunc uint64_t __bswap_64(uint64_t v)
+{
+ return ((uint64_t)__bswap_32(v) << 32) | __bswap_32(v >> 32);
+}
+
+#define bswap_64(x) (__builtin_constant_p(x) ? \
+ __bswap_64_macro(x) : __bswap_64(x))
+
+#endif /* byteswap.h */
diff --git a/com32/include/cli.h b/com32/include/cli.h
new file mode 100644
index 00000000..eee4576f
--- /dev/null
+++ b/com32/include/cli.h
@@ -0,0 +1,20 @@
+#ifndef CLI_H
+#define CLI_H
+
+#define MAX_CMD_HISTORY 64
+#define COMMAND_DELIM " \t\n" // Whitespace delimiters
+#define MAX_COMMAND_ARGS 40
+
+struct cli_command {
+ struct list_head list;
+ char *command;
+};
+
+extern void clear_screen(void);
+extern int mygetkey(clock_t timeout);
+extern const char *edit_cmdline(const char *input, int top /*, int width */ ,
+ int (*pDraw_Menu) (int, int, int),
+ void (*show_fkey) (int), bool *);
+
+extern struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu;
+#endif
diff --git a/com32/include/com32.h b/com32/include/com32.h
index 6b142082..795b9fba 100644
--- a/com32/include/com32.h
+++ b/com32/include/com32.h
@@ -135,10 +135,22 @@ char *lstrdup(const char *);
* specific segment. OFFS_VALID() will return whether or not the
* pointer is actually reachable from the target segment.
*/
+#if defined(DEBUG) && (defined(__COM32__) || defined(__SYSLINUX_CORE__))
+__noreturn __bad_SEG(const volatile void *);
+
static inline uint16_t SEG(const volatile void *__p)
{
+ if (__unlikely((uintptr_t)__p > 0xfffff))
+ __bad_SEG(__p);
+
return (uint16_t) (((uintptr_t) __p) >> 4);
}
+#else
+static inline uint16_t SEG(const volatile void *__p)
+{
+ return (uint16_t) (((uintptr_t) __p) >> 4);
+}
+#endif
static inline uint16_t OFFS(const volatile void *__p)
{
@@ -194,4 +206,6 @@ static inline far_ptr_t FAR_PTR(void *__ptr)
return __fptr;
}
+extern const char *com32_cmdline(void);
+
#endif /* _COM32_H */
diff --git a/com32/include/dprintf.h b/com32/include/dprintf.h
index b8a3b84c..1b539698 100644
--- a/com32/include/dprintf.h
+++ b/com32/include/dprintf.h
@@ -5,6 +5,12 @@
#ifndef _DPRINTF_H
#define _DPRINTF_H
+#include <syslinux/debug.h>
+
+#if !defined(DEBUG_PORT) && !defined(DEBUG_STDIO)
+# undef DEBUG
+#endif
+
#ifdef DEBUG
# include <stdio.h>
@@ -12,15 +18,22 @@
# ifdef DEBUG_STDIO
# define dprintf printf
# define vdprintf vprintf
+# define ddprintf dprintf
# else
void dprintf(const char *, ...);
void vdprintf(const char *, va_list);
+# define ddprintf(...) { printf(__VA_ARGS__); dprintf(__VA_ARGS__); }
# endif
#else
-# define dprintf(fmt, ...) ((void)(0))
-# define vdprintf(fmt, ap) ((void)(0))
+#define dprintf(...) \
+ if (syslinux_debug_enabled) \
+ printf(__VA_ARGS__)
+#define vdprintf(fmt, ap) \
+ if (syslinux_debug_enabled) \
+ vprintf(fmt, ap)
+#define ddprintf printf
#endif /* DEBUG */
diff --git a/com32/include/errno.h b/com32/include/errno.h
index 36690bf6..40bf2ecc 100644
--- a/com32/include/errno.h
+++ b/com32/include/errno.h
@@ -131,4 +131,25 @@ extern int errno;
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
+/* lwIP nameserver query return codes */
+#define ENSROK 0 /* DNS server returned answer with no data */
+#define ENSRNODATA 160 /* DNS server returned answer with no data */
+#define ENSRFORMERR 161 /* DNS server claims query was misformatted */
+#define ENSRSERVFAIL 162 /* DNS server returned general failure */
+#define ENSRNOTFOUND 163 /* Domain name not found */
+#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */
+#define ENSRREFUSED 165 /* DNS server refused query */
+#define ENSRBADQUERY 166 /* Misformatted DNS query */
+#define ENSRBADNAME 167 /* Misformatted domain name */
+#define ENSRBADFAMILY 168 /* Unsupported address family */
+#define ENSRBADRESP 169 /* Misformatted DNS reply */
+#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */
+#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */
+#define ENSROF 172 /* End of file */
+#define ENSRFILE 173 /* Error reading file */
+#define ENSRNOMEM 174 /* Out of memory */
+#define ENSRDESTRUCTION 175 /* Application terminated lookup */
+#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */
+#define ENSRCNAMELOOP 177 /* Domain name is too long */
+
#endif /* _ERRNO_H */
diff --git a/com32/include/fcntl.h b/com32/include/fcntl.h
index b691b5cd..c8a9cb52 100644
--- a/com32/include/fcntl.h
+++ b/com32/include/fcntl.h
@@ -14,6 +14,7 @@
#define O_RDONLY 1
#define O_WRONLY 2
#define O_RDWR 3
+#define O_DIRECTORY 010
#define O_CREAT 0100
#define O_EXCL 0200
#define O_TRUNC 01000
diff --git a/com32/include/hw/vga.h b/com32/include/hw/vga.h
new file mode 100644
index 00000000..0ebd2e25
--- /dev/null
+++ b/com32/include/hw/vga.h
@@ -0,0 +1,104 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2012 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef _HW_VGA_H
+#define _HW_VGA_H 1
+
+#include <sys/io.h>
+
+/* These are relative to the CRTC base address */
+#define VGA_CRTC_ADDR 0x4
+#define VGA_CRTC_DATA 0x5
+#define VGA_CRTC_IX_HORIZ_TOTAL 0x00
+#define VGA_CRTC_IX_END_HORIZ_DISPLAY 0x01
+#define VGA_CRTC_IX_START_HORIZ_BLANK 0x02
+#define VGA_CRTC_IX_END_HORIZ_BLANK 0x03
+#define VGA_CRTC_IX_START_HORIZ_RETR 0x04
+#define VGA_CRTC_IX_END_HORIZ_RETR 0x05
+#define VGA_CRTC_IX_VERT_TOTAL 0x06
+#define VGA_CRTC_IX_OVERFLOW 0x07
+#define VGA_CRTC_IX_PRESET_ROW_SCAN 0x08
+#define VGA_CRTC_IX_MAX_SCAN_LINE 0x09
+#define VGA_CRTC_IX_CURSOR_START 0x0a
+#define VGA_CRTC_IX_CURSOR_END 0x0b
+#define VGA_CRTC_IX_START_ADDR_HIGH 0x0c
+#define VGA_CRTC_IX_START_ADDR_LOW 0x0d
+#define VGA_CRTC_IX_CURSOR_POS_HIGH 0x0e
+#define VGA_CRTC_IX_CURSOR_POS_LOW 0x0f
+#define VGA_CRTC_IX_START_VERT_RETR 0x10
+#define VGA_CRTC_IX_END_VERT_RETR 0x11
+#define VGA_CRTC_IX_END_VERT_DISPLAY 0x12
+#define VGA_CRTC_IX_OFFSET 0x13
+#define VGA_CRTC_IX_UNDERLINE 0x14
+#define VGA_CRTC_IX_START_VERT_BLANK 0x15
+#define VGA_CRTC_IX_END_VERT_BLANK 0x16
+#define VGA_CRTC_IX_MODE_CONTROL 0x17
+#define VGA_CRTC_IX_LINE_COMPARE 0x18
+#define VGA_CRTC_INPUT_STATUS_1 0xa
+#define VGA_CRTC_FEATURE_CONTROL_WRITE 0xa
+
+#define VGA_ATTR_ADDR_DATA 0x3c0
+#define VGA_ATTR_DATA_READ 0x3c1
+/* 0x00-0x0f are 16->64 palette registers */
+#define VGA_ATTR_IX_MODE_CONTROL 0x10
+#define VGA_ATTR_IX_OVERSCAN 0x11
+#define VGA_ATTR_IX_COLOR_PLANE_ENABLE 0x12
+#define VGA_ATTR_IX_HORIZ_PIXEL_PAN 0x13
+#define VGA_ADDR_IX_COLOR_SELECT 0x14
+#define VGA_INPUT_STATUS_0 0x3c2
+#define VGA_MISC_OUTPUT_WRITE 0x3c2
+#define VGA_SEQ_ADDR 0x3c4
+#define VGA_SEQ_DATA 0x3c5
+#define VGA_SEQ_IX_RESET 0
+#define VGA_SEQ_IX_CLOCKMODE 1
+#define VGA_SEQ_IX_MAP_MASK 2
+#define VGA_SEQ_IX_CHAR_MAP 3
+#define VGA_SEQ_IX_SEQ_MEM_MODE 4
+#define VGA_DAC_STATE 0x3c7
+#define VGA_DAC_ADDR_READ_MODE 0x3c7
+#define VGA_DAC_ADDR_WRITE_MODE 0x3c8
+#define VGA_DAC_DATA 0x3c9
+#define VGA_FEATURE_CONTROL_READ 0x3ca
+#define VGA_MISC_OUTPUT_READ 0x3cc
+#define VGA_GC_ADDR 0x3ce
+#define VGA_GC_DATA 0x3cf
+#define VGA_GC_IX_SET_RESET 0
+#define VGA_GC_IX_ENABLE_SET_RESET 1
+#define VGA_GC_IX_COLOR_COMPARE 2
+#define VGA_GC_IX_DATA_ROTATE 3
+#define VGA_GC_IX_READ_MAP_SELECT 4
+#define VGA_GC_IX_GRAPHICS_MODE 5
+#define VGA_GC_IX_MISC_GRAPHICS 6
+#define VGA_GC_IX_COLOR_DONT_CARE 7
+#define VGA_GC_IX_BIT_MASK 8
+
+static inline uint16_t vga_crtc_base(void)
+{
+ return 0x3b0 + ((inb(VGA_MISC_OUTPUT_READ) & 1) << 5);
+}
+
+#endif /* _HW_VGA_H */
diff --git a/com32/include/klibc/compiler.h b/com32/include/klibc/compiler.h
index 5ac21185..e8548b59 100644
--- a/com32/include/klibc/compiler.h
+++ b/com32/include/klibc/compiler.h
@@ -108,6 +108,16 @@
# define __unusedfunc
#endif
+/* Declare a variable or data structure as unused. */
+#ifdef __GNUC__
+# define __unused __attribute__((unused))
+#else
+# define __unused
+#endif
+
+/* Used symbol */
+#define __used __attribute__((used))
+
/* Constructors and destructors */
#define __constructor __attribute__((constructor))
#define __destructor __attribute__((destructor))
@@ -126,4 +136,9 @@
#define __common __attribute__((common))
#define __nocommon __attribute__((nocommon))
+/* Weak symbols */
+#define __weak __attribute__((weak))
+
+#define __export __attribute__((visibility("default")))
+
#endif
diff --git a/com32/include/linux/list.h b/com32/include/linux/list.h
new file mode 100644
index 00000000..157ded10
--- /dev/null
+++ b/com32/include/linux/list.h
@@ -0,0 +1,464 @@
+// This list structure implementation is adapted from the list implementation
+// on the Linux kernel.
+
+// Original source:
+// http://git.kernel.org/?p=linux/kernel/git/stable/linux-2.6.25.y.git;a=blob_plain;f=include/linux/list.h;hb=HEAD
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+* @pos: the type * to use as a loop cursor.
+* @n: another type * to use as temporary storage
+* @head: the head for your list.
+* @member: the name of the list_struct within the struct.
+*/
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+
+#endif
diff --git a/com32/menu/menu.h b/com32/include/menu.h
index 1db4d7c9..bc0182f7 100644
--- a/com32/menu/menu.h
+++ b/com32/include/menu.h
@@ -87,13 +87,10 @@ enum kernel_type {
KT_BSS, /* Boot sector with patch */
KT_PXE, /* PXE NBP */
KT_FDIMAGE, /* Floppy disk image */
- KT_COMBOOT, /* COMBOOT image */
KT_COM32, /* COM32 image */
KT_CONFIG, /* Configuration file */
};
-extern const char *const kernel_types[];
-
/* Configurable integer parameters */
enum parameter_number {
P_WIDTH,
@@ -187,6 +184,7 @@ extern int shiftkey;
extern int hiddenmenu;
extern int clearmenu;
extern long long totaltimeout;
+extern clock_t kbdtimeout;
extern const char *hide_key[KEY_MAX];
void parse_configs(char **argv);
@@ -227,10 +225,10 @@ extern const int message_base_color;
extern const char *current_background;
void set_background(const char *new_background);
-/* execute.c */
-void execute(const char *cmdline, enum kernel_type type);
-
/* drain.c */
void drain_keyboard(void);
+/* chainboot.c */
+void chainboot_file(const char *file, enum kernel_type type);
+
#endif /* MENU_H */
diff --git a/com32/include/netinet/in.h b/com32/include/netinet/in.h
index d2af351f..b24f8046 100644
--- a/com32/include/netinet/in.h
+++ b/com32/include/netinet/in.h
@@ -3,49 +3,17 @@
/* COM32 will be running on an i386 platform */
-#include <stdint.h>
#include <klibc/compiler.h>
#include <klibc/extern.h>
-
-#define __htons_macro(v) ((uint16_t) \
- (((uint16_t)(v) << 8) | \
- ((uint16_t)(v) >> 8)))
-
-static inline __constfunc uint16_t __htons(uint16_t v)
-{
- return __htons_macro(v);
-}
-
-#define htons(x) (__builtin_constant_p(x) ? __htons_macro(x) : __htons(x))
-#define ntohs(x) htons(x)
-
-#define __htonl_macro(v) ((uint32_t) \
- ((((uint32_t)(v) & 0x000000ff) << 24) | \
- (((uint32_t)(v) & 0x0000ff00) << 8) | \
- (((uint32_t)(v) & 0x00ff0000) >> 8) | \
- (((uint32_t)(v) & 0xff000000) >> 24)))
-
-static inline __constfunc uint32_t __htonl(uint32_t v)
-{
- asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0"
- : "+q" (v));
- return v;
-}
-
-#define htonl(x) (__builtin_constant_p(x) ? __htonl_macro(x) : __htonl(x))
-#define ntohl(x) htonl(x)
-
-#define __htonq_macro(v) ((uint64_t) \
- (((uint64_t)__htonl_macro((uint32_t)(v)) << 32) | \
- (__htonl_macro((uint32_t)((uint64_t)(v) >> 32)))))
-
-static inline __constfunc uint64_t __htonq(uint64_t v)
-{
- return ((uint64_t)__htonl(v) << 32) | __htonl(v >> 32);
-}
-
-#define htonq(x) (__builtin_constant_p(x) ? __htonq_macro(x) : __htonq(x))
-#define ntohq(x) htonq(x)
+#include <stdint.h>
+#include <byteswap.h>
+
+#define htons(x) bswap_16(x)
+#define ntohs(x) bswap_16(x)
+#define htonl(x) bswap_32(x)
+#define ntohl(x) bswap_32(x)
+#define htonq(x) bswap_64(x)
+#define ntohq(x) bswap_64(x)
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
diff --git a/com32/menu/refstr.h b/com32/include/refstr.h
index 7001d407..7001d407 100644
--- a/com32/menu/refstr.h
+++ b/com32/include/refstr.h
diff --git a/com32/include/sort.h b/com32/include/sort.h
new file mode 100644
index 00000000..0b495482
--- /dev/null
+++ b/com32/include/sort.h
@@ -0,0 +1,18 @@
+/*
+ * sort.h - Quick sort module API definitions
+ *
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#ifndef SORT_H_
+#define SORT_H_
+
+/**
+ * quick_sort - In place sort of an array of numbers.
+ * @nums: Pointer to the array
+ * @count: The number count in the array
+ */
+extern void quick_sort(int *nums, int count);
+
+#endif /* SORT_H_ */
diff --git a/com32/include/stddef.h b/com32/include/stddef.h
index 125d2352..f52d62f3 100644
--- a/com32/include/stddef.h
+++ b/com32/include/stddef.h
@@ -21,4 +21,12 @@
#undef offsetof
#define offsetof(t,m) ((size_t)&((t *)0)->m)
+#undef container_of
+/*
+ * The container_of construct: if p is a pointer to member m of
+ * container class c, then return a pointer to the container of which
+ * *p is a member.
+ */
+#define container_of(p, c, m) ((c *)((char *)(p) - offsetof(c,m)))
+
#endif /* _STDDEF_H */
diff --git a/com32/include/stdio.h b/com32/include/stdio.h
index f37bdd9e..813a0edc 100644
--- a/com32/include/stdio.h
+++ b/com32/include/stdio.h
@@ -100,6 +100,11 @@ __extern int vsnprintf(char *, size_t n, const char *, va_list);
__extern int asprintf(char **, const char *, ...);
__extern int vasprintf(char **, const char *, va_list);
+#define mp(f, x...) \
+ printf("[%s()]: " f "\n", __func__,##x)
+#define mpi() mp("enter")
+#define mpo() mp("exit")
+
/* No buffering, so no flushing needed */
static __inline__ int fflush(FILE * __f)
{
@@ -114,4 +119,27 @@ __extern void perror(const char *);
__extern int rename(const char *, const char *);
+/*
+ * unhexchar: Convert a hexadecimal digit to the equivalent number
+ *
+ * Returns 0 if 'data' was converted succesfully, -1 otherwise.
+ */
+static inline int unhexchar(unsigned char *data)
+{
+ unsigned char num = *data;
+
+ if (num >= '0' && num <= '9') {
+ *data = num - '0';
+ return 0;
+ } else {
+ num |= 0x20; /* upper case -> lower case */
+ if (num >= 'a' && num <= 'f') {
+ *data = num - 'a' + 10;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
#endif /* _STDIO_H */
diff --git a/com32/include/sys/cpu.h b/com32/include/sys/cpu.h
index 53a6250e..807e13c4 100644
--- a/com32/include/sys/cpu.h
+++ b/com32/include/sys/cpu.h
@@ -121,22 +121,45 @@ static inline void wrmsr(uint64_t v, uint32_t msr)
static inline void cpu_relax(void)
{
- asm volatile("rep ; nop");
+ asm volatile("rep ; nop" : : : "memory");
}
static inline void hlt(void)
{
- asm volatile("hlt");
+ asm volatile("hlt" : : : "memory");
}
static inline void cli(void)
{
- asm volatile("cli");
+ asm volatile("cli" : : : "memory");
}
static inline void sti(void)
{
- asm volatile("sti");
+ asm volatile("sti" : : : "memory");
}
+typedef unsigned long irq_state_t;
+
+static inline irq_state_t irq_state(void)
+{
+ irq_state_t __st;
+
+ asm volatile("pushfl ; popl %0" : "=rm" (__st) : : "memory");
+ return __st;
+}
+
+static inline irq_state_t irq_save(void)
+{
+ irq_state_t __st = irq_state();
+ cli();
+ return __st;
+}
+
+static inline void irq_restore(irq_state_t __st)
+{
+ asm volatile("pushl %0 ; popfl" : : "rm" (__st) : "memory");
+}
+
+
#endif
diff --git a/com32/include/sys/elfcommon.h b/com32/include/sys/elfcommon.h
index 2489e3c0..8d6ddb05 100644
--- a/com32/include/sys/elfcommon.h
+++ b/com32/include/sys/elfcommon.h
@@ -59,32 +59,108 @@
#define EM_S390_OLD 0xA390 /* Obsolete interrim value for S/390 */
/* Dynamic type values */
-#define DT_NULL 0
-#define DT_NEEDED 1
-#define DT_PLTRELSZ 2
-#define DT_PLTGOT 3
-#define DT_HASH 4
-#define DT_STRTAB 5
-#define DT_SYMTAB 6
-#define DT_RELA 7
-#define DT_RELASZ 8
-#define DT_RELAENT 9
-#define DT_STRSZ 10
-#define DT_SYMENT 11
-#define DT_INIT 12
-#define DT_FINI 13
-#define DT_SONAME 14
-#define DT_RPATH 15
-#define DT_SYMBOLIC 16
-#define DT_REL 17
-#define DT_RELSZ 18
-#define DT_RELENT 19
-#define DT_PLTREL 20
-#define DT_DEBUG 21
-#define DT_TEXTREL 22
-#define DT_JMPREL 23
-#define DT_LOPROC 0x70000000
-#define DT_HIPROC 0x7fffffff
+#define DT_NULL 0 /* Marks end of dynamic section */
+#define DT_NEEDED 1 /* Name of needed library */
+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
+#define DT_PLTGOT 3 /* Processor defined value */
+#define DT_HASH 4 /* Address of symbol hash table */
+#define DT_STRTAB 5 /* Address of string table */
+#define DT_SYMTAB 6 /* Address of symbol table */
+#define DT_RELA 7 /* Address of Rela relocs */
+#define DT_RELASZ 8 /* Total size of Rela relocs */
+#define DT_RELAENT 9 /* Size of one Rela reloc */
+#define DT_STRSZ 10 /* Size of string table */
+#define DT_SYMENT 11 /* Size of one symbol table entry */
+#define DT_INIT 12 /* Address of init function */
+#define DT_FINI 13 /* Address of termination function */
+#define DT_SONAME 14 /* Name of shared object */
+#define DT_RPATH 15 /* Library search path (deprecated) */
+#define DT_SYMBOLIC 16 /* Start symbol search here */
+#define DT_REL 17 /* Address of Rel relocs */
+#define DT_RELSZ 18 /* Total size of Rel relocs */
+#define DT_RELENT 19 /* Size of one Rel reloc */
+#define DT_PLTREL 20 /* Type of reloc in PLT */
+#define DT_DEBUG 21 /* For debugging; unspecified */
+#define DT_TEXTREL 22 /* Reloc might modify .text */
+#define DT_JMPREL 23 /* Address of PLT relocs */
+#define DT_BIND_NOW 24 /* Process relocations of object */
+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
+#define DT_RUNPATH 29 /* Library search path */
+#define DT_FLAGS 30 /* Flags for the object being loaded */
+#define DT_ENCODING 32 /* Start of encoded range */
+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
+#define DT_NUM 34 /* Number used */
+#define DT_LOOS 0x6000000d /* Start of OS-specific */
+#define DT_HIOS 0x6ffff000 /* End of OS-specific */
+#define DT_LOPROC 0x70000000 /* Start of processor-specific */
+#define DT_HIPROC 0x7fffffff /* End of processor-specific */
+
+#define DT_VALRNGLO 0x6ffffd00
+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */
+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */
+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */
+#define DT_CHECKSUM 0x6ffffdf8
+#define DT_PLTPADSZ 0x6ffffdf9
+#define DT_MOVEENT 0x6ffffdfa
+#define DT_MOVESZ 0x6ffffdfb
+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */
+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
+ the following DT_* entry. */
+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
+#define DT_VALRNGHI 0x6ffffdff
+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
+#define DT_VALNUM 12
+
+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+
+ If any adjustment is made to the ELF object after it has been
+ built these entries will need to be adjusted. */
+#define DT_ADDRRNGLO 0x6ffffe00
+#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
+#define DT_TLSDESC_PLT 0x6ffffef6
+#define DT_TLSDESC_GOT 0x6ffffef7
+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
+#define DT_CONFIG 0x6ffffefa /* Configuration information. */
+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
+#define DT_AUDIT 0x6ffffefc /* Object auditing. */
+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */
+#define DT_MOVETAB 0x6ffffefe /* Move table. */
+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
+#define DT_ADDRRNGHI 0x6ffffeff
+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
+#define DT_ADDRNUM 11
+
+/* The versioning entry types. The next are defined as part of the
+ GNU extension. */
+#define DT_VERSYM 0x6ffffff0
+
+#define DT_RELACOUNT 0x6ffffff9
+#define DT_RELCOUNT 0x6ffffffa
+
+/* These were chosen by Sun. */
+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
+#define DT_VERDEF 0x6ffffffc /* Address of version definition
+ table */
+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
+#define DT_VERNEED 0x6ffffffe /* Address of table with needed
+ versions */
+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
+#define DT_VERSIONTAGNUM 16
+
+/* Sun added these machine-independent extensions in the "processor-specific"
+ range. Be compatible. */
+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
+#define DT_FILTER 0x7fffffff /* Shared object to get values from */
+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
+#define DT_EXTRANUM 3
/* Auxilliary table entries */
#define AT_NULL 0 /* end of vector */
@@ -147,6 +223,52 @@
#define SHN_COMMON 0xfff2
#define SHN_HIRESERVE 0xffff
+/* Symbol table definitions */
+
+/* How to extract and insert information held in the st_info field. */
+
+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
+#define ELF32_ST_TYPE(val) ((val) & 0xf)
+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
+
+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
+
+/* Legal values for ST_BIND subfield of st_info (symbol binding). */
+
+#define STB_LOCAL 0 /* Local symbol */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* Weak symbol */
+#define STB_NUM 3 /* Number of defined types. */
+#define STB_LOOS 10 /* Start of OS-specific */
+#define STB_HIOS 12 /* End of OS-specific */
+#define STB_LOPROC 13 /* Start of processor-specific */
+#define STB_HIPROC 15 /* End of processor-specific */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_NOTYPE 0 /* Symbol type is unspecified */
+#define STT_OBJECT 1 /* Symbol is a data object */
+#define STT_FUNC 2 /* Symbol is a code object */
+#define STT_SECTION 3 /* Symbol associated with a section */
+#define STT_FILE 4 /* Symbol's name is file name */
+#define STT_COMMON 5 /* Symbol is a common data object */
+#define STT_TLS 6 /* Symbol is thread-local data object*/
+#define STT_NUM 7 /* Number of defined types. */
+#define STT_LOOS 10 /* Start of OS-specific */
+#define STT_HIOS 12 /* End of OS-specific */
+#define STT_LOPROC 13 /* Start of processor-specific */
+#define STT_HIPROC 15 /* End of processor-specific */
+
+
+/* Symbol table indices are found in the hash buckets and chain table
+ of a symbol hash table section. This special index value indicates
+ the end of a chain, meaning no further symbols are found in that bucket. */
+
+#define STN_UNDEF 0 /* End of a chain. */
+
/* Lenght of magic at the start of a file */
#define EI_NIDENT 16
@@ -184,4 +306,59 @@
#define ELFOSABI_NONE 0
#define ELFOSABI_LINUX 3
-#endif /* _SYS_ELFCOMMON_H */
+/* Intel 80386 specific definitions. */
+
+/* i386 relocs. */
+
+#define R_386_NONE 0 /* No reloc */
+#define R_386_32 1 /* Direct 32 bit */
+#define R_386_PC32 2 /* PC relative 32 bit */
+#define R_386_GOT32 3 /* 32 bit GOT entry */
+#define R_386_PLT32 4 /* 32 bit PLT address */
+#define R_386_COPY 5 /* Copy symbol at runtime */
+#define R_386_GLOB_DAT 6 /* Create GOT entry */
+#define R_386_JMP_SLOT 7 /* Create PLT entry */
+#define R_386_RELATIVE 8 /* Adjust by program base */
+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
+#define R_386_32PLT 11
+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
+ block offset */
+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
+ offset */
+#define R_386_TLS_LE 17 /* Offset relative to static TLS
+ block */
+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
+ general dynamic thread local data */
+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
+ local dynamic thread local data
+ in LE code */
+#define R_386_16 20
+#define R_386_PC16 21
+#define R_386_8 22
+#define R_386_PC8 23
+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
+ thread local data */
+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
+#define R_386_TLS_GD_CALL 26 /* Relocation for call to
+ __tls_get_addr() */
+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
+ thread local data in LE code */
+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
+ __tls_get_addr() in LDM code */
+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
+ block offset */
+#define R_386_TLS_LE_32 34 /* Negated offset relative to static
+ TLS block */
+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
+/* Keep this the last entry. */
+#define R_386_NUM 38
+
+#endif /* _SYS_ELFCOMMON_H */
diff --git a/com32/include/sys/exec.h b/com32/include/sys/exec.h
new file mode 100644
index 00000000..f4559d15
--- /dev/null
+++ b/com32/include/sys/exec.h
@@ -0,0 +1,79 @@
+/*
+ * exec.h
+ *
+ * Created on: Aug 14, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#ifndef EXEC_H_
+#define EXEC_H_
+
+#include <sys/module.h>
+#include <stdlib.h>
+
+/**
+ * EXEC_ROOT_NAME - The name of the ELF module associated with the COM32 module.
+ *
+ * This is a shallow ELF module, that contains only the symbol table for
+ * the code and data sections of the loaded COM32 root module.
+ */
+#define EXEC_ROOT_NAME "_root_.c32"
+
+/**
+ * spawn_load - Load a library module or executes an executable one
+ * @name the name of the library/executable to use, including the extension
+ * (e.g. 'sort.c32')
+ * @argc: the number of string arguments in @argv
+ * @argv: a NULL-terminated vector of string arguments, starting with
+ * the program name.
+ *
+ * This procedure in essence loads takes the name of a module and checks to see what
+ * kind of module it is ( executable or library ), after which is performs the
+ * appropriate action, either spawning or simply loading the module into memory.
+ */
+extern int spawn_load(const char *name, int argc, char **argv);
+
+/**
+ * spawnv - Executes a program in the current environment.
+ * @name: the name of the program to spawn, including the extension
+ * (e.g. 'hello.c32')
+ * @argv: a NULL-terminated vector of string arguments, starting with
+ * the program name.
+ *
+ * A program is an ELF module that contains a main routine. A program is
+ * loaded into memory, executed, then unloaded, thus remaining in memory only
+ * while the main() function is executing. A program also defines a
+ * memory allocation context, and a simple garbage collection mechanism
+ * it thus provided. This is done by internally associating with the program
+ * module each pointer returned by malloc(). After the program finishes
+ * its execution, all the unallocated memory pertaining to the program
+ * is automatically cleaned up.
+ *
+ * Note that this association takes place both for the allocations happening
+ * directly in the program, or indirectly through a library function. Libraries
+ * do not create allocation contexts, thus each allocation they made belong
+ * to the innermost calling program.
+ */
+extern int spawnv(const char *name, const char **argv);
+
+/**
+ * spawnl - Executes a program in the current environment.
+ * @name: the name of the program to spawn, including the extension
+ * (e.g. 'hello.c32')
+ * @arg: the first argument (argv[0]) to be passed to the main function
+ * of the program
+ * @...: optional subsequent arguments that are passed o the main function
+ * of the program
+ *
+ * This is another version of the spawn routine. Please see 'spawnv' for
+ * a full presentation.
+ */
+extern int spawnl(const char *name, const char *arg, ...);
+
+/**
+ * exec_term - Releases the resources of the execution environment.
+ */
+extern void exec_term(void);
+
+
+#endif /* EXEC_H_ */
diff --git a/com32/include/sys/module.h b/com32/include/sys/module.h
new file mode 100644
index 00000000..8d144203
--- /dev/null
+++ b/com32/include/sys/module.h
@@ -0,0 +1,374 @@
+/**
+ * syslinux/module.h
+ *
+ * Dynamic ELF modules definitions and services.
+ */
+
+
+#ifndef MODULE_H_
+#define MODULE_H_
+
+#include <stdio.h>
+#include <elf.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <linux/list.h>
+
+/*
+ * The maximum length of the module file name (including path), stored
+ * in the struct module descriptor.
+ */
+#define MODULE_NAME_SIZE 256
+
+/*
+ * Some common information about what kind of modules we're dealing with
+ */
+#define EXEC_MODULE 0
+#define LIB_MODULE 1
+
+#define MAX_NR_DEPS 64
+
+/*
+ * Initialization and finalization function signatures
+ */
+
+/**
+ * module_main_t - pointer to an entry routine
+ *
+ * The entry routine is present only in executable modules, and represents
+ * the entry point for the program.
+ */
+typedef int (*module_main_t)(int, char**);
+
+/**
+ * module_ctor_t - pointer to a constructor or destructor routine
+ *
+ * A module may have multiple routines that need to be executed before
+ * or after the main routine. These are the constructors and
+ * destructors, respectively.
+ */
+typedef void (*module_ctor_t) (void);
+
+/**
+ * struct elf_module - structure encapsulating a module loaded in memory.
+ *
+ * Each SYSLINUX ELF module must have an associated struct elf_module descriptor
+ * that keeps track of memory allocations, symbol information, and various other
+ * resources needed by the module itself or by other modules that depend on it.
+ *
+ * There are two types of modules:
+ * - regular modules, which are actual memory images of a loaded & linked shared
+ * object (ELF file). Memory is reserved for the struct elf_module structure itself
+ * and for the object loadable sections read from the file.
+ * - shallow modules, which are not associated with an ELF shared object, but contain
+ * metainformation about a memory region already present and containing the
+ * actual code and data. One particular usage of shallow modules is to access
+ * symbol information from the root COM32 module loaded by the SYSLINUX core.
+ * As their name suggests, memory is reserved only for the elf_module structure
+ * itself and optionally for a usually small memory region containing metainformation
+ * (symbol information).
+ *
+ * Module descriptors are related to each other through dependency information. A module
+ * can depend on symbols from other modules, and in turn it can provide symbols used
+ * by other dependant modules. This relationship can be described as a directed
+ * acyclic graph (DAG). The graph is stored using double linked lists of
+ * predecessors and successors. There is also a global linked list containing all
+ * the modules currently loaded.
+ */
+struct atexit;
+struct elf_module {
+ char name[MODULE_NAME_SIZE]; // The module name
+
+ bool shallow; // Whether the module contains any code
+
+ struct list_head required; // Head of the required modules list
+ struct list_head dependants; // Head of module dependants list
+ struct list_head list; // The list entry in the module list
+
+ module_ctor_t *ctors; // module constructors
+ module_ctor_t *dtors; // module destructors
+ module_main_t main_func; // The main function (for executable modules)
+
+ void *module_addr; // The module location in the memory
+ Elf32_Addr base_addr; // The base address of the module
+ Elf32_Word module_size; // The module size in memory
+
+ Elf32_Word *hash_table; // The symbol hash table
+ Elf32_Word *ghash_table; // The GNU style hash table
+ char *str_table; // The string table
+ void *sym_table; // The symbol table
+ void *got; // The Global Offset Table
+ Elf32_Dyn *dyn_table; // Dynamic loading information table
+
+ Elf32_Word strtable_size; // The size of the string table
+ Elf32_Word syment_size; // The size of a symbol entry
+ Elf32_Word symtable_size; // The size of the symbol table
+
+
+ union {
+ // Transient - Data available while the module is loading
+ struct {
+ FILE *_file; // The file object of the open file
+ Elf32_Off _cr_offset; // The current offset in the open file
+ } l;
+
+ // Process execution data
+ struct {
+ jmp_buf process_exit; // Exit state
+ struct atexit *atexit_list; // atexit() chain
+ } x;
+ } u;
+
+ // ELF DT_NEEDED entries for this module
+ int nr_needed;
+ Elf32_Word needed[MAX_NR_DEPS];
+};
+
+/**
+ * struct module_dep - structure encapsulating a module dependency need
+ *
+ * This structure represents an item in a double linked list of predecessors or
+ * successors. The item contents is a pointer to the corresponding module descriptor.
+ */
+struct module_dep {
+ struct list_head list; // The list entry in the dependency list
+
+ struct elf_module *module; // The target module descriptor
+};
+
+
+/**
+ * Unload all modules that have been loaded since @name.
+ *
+ * Returns the struct elf_module * for @name or %NULL if no modules
+ * have been loaded since @name.
+ */
+extern struct elf_module *unload_modules_since(const char *name);
+
+extern FILE *findpath(char *name);
+
+
+#ifdef DYNAMIC_MODULE
+
+/*
+ * This portion is included by dynamic (ELF) module source files.
+ */
+
+#define MODULE_INIT(fn) static module_init_t __module_init \
+ __used __attribute__((section(".ctors_modinit"))) = fn
+
+#define MODULE_EXIT(fn) static module_exit_t __module_exit \
+ __used __attribute__((section(".dtors_modexit"))) = fn
+
+#else
+
+/*
+ * This portion is included by the core COM32 module.
+ */
+
+/*
+ * Accepted values for various ELF header parameters found in an ELF dynamic
+ * object.
+ */
+#define MODULE_ELF_CLASS ELFCLASS32 // 32-bit modules
+#define MODULE_ELF_CLASS_SIZE 32 // Size of a word value
+#define MODULE_ELF_DATA ELFDATA2LSB // Word endianess
+#define MODULE_ELF_VERSION EV_CURRENT // Object version
+#define MODULE_ELF_TYPE ET_DYN // Executable type (shared object - .so)
+#define MODULE_ELF_MACHINE EM_386 // Target architecture
+
+/**
+ * Names of symbols with special meaning (treated as special cases at linking)
+ */
+#define MODULE_ELF_INIT_PTR "__module_init_ptr" // Initialization pointer symbol name
+#define MODULE_ELF_EXIT_PTR "__module_exit_ptr" // Finalization pointer symbol name
+#define MODULE_ELF_MAIN_PTR "__module_main_ptr" // Entry pointer symbol name
+
+/**
+ * modules_head - A global linked list containing all the loaded modules.
+ */
+extern struct list_head modules_head;
+
+
+/**
+ * for_each_module - iterator loop through the list of loaded modules.
+ */
+#define for_each_module(m) list_for_each_entry(m, &modules_head, list)
+
+/**
+ * for_each_module - iterator loop through the list of loaded modules safe against removal.
+ */
+#define for_each_module_safe(m, n) \
+ list_for_each_entry_safe(m, n, &modules_head, list)
+
+/**
+ * module_current - return the module at the head of the module list.
+ */
+static inline struct elf_module *module_current(void)
+{
+ struct elf_module *head;
+
+ head = list_entry((&modules_head)->next, typeof(*head), list);
+ return head;
+}
+
+/**
+ * modules_init - initialize the module subsystem.
+ *
+ * This function must be called before any module operation is to be performed.
+ */
+extern int modules_init(void);
+
+
+/**
+ * modules_term - releases all resources pertaining to the module subsystem.
+ *
+ * This function should be called after all module operations.
+ */
+extern void modules_term(void);
+
+
+/**
+ * module_alloc - reserves space for a new module descriptor.
+ * @name: the file name of the module to be loaded.
+ *
+ * The function simply allocates a new module descriptor and initializes its fields
+ * in order to be used by subsequent loading operations.
+ */
+extern struct elf_module *module_alloc(const char *name);
+
+
+/**
+ * module_load - loads a regular ELF module into memory.
+ * @module: the module descriptor returned by module_alloc.
+ *
+ * The function reads the module file, checks whether the file has a
+ * valid structure, then loads into memory the code and the data and performs
+ * any symbol relocations. A module dependency is created automatically when the
+ * relocated symbol is defined in a different module.
+ *
+ * The function returns 0 if the operation is completed successfully, and
+ * a non-zero value if an error occurs. Possible errors include invalid module
+ * structure, missing symbol definitions (unsatisfied dependencies) and memory
+ * allocation issues.
+ */
+extern int module_load(struct elf_module *module);
+
+
+/**
+ * module_unload - unloads the module from the system.
+ * @module: the module descriptor structure.
+ *
+ * The function checks to see whether the module can be safely
+ * removed, then it executes any destructors and releases all the
+ * associated memory. This function can be applied both for standard
+ * modules and for shallow modules.
+ *
+ * A module can be safely removed from the system when no other modules reference
+ * symbols from it.
+ */
+extern int module_unload(struct elf_module *module);
+
+/**
+ * _module_unload - unloads the module without running destructors
+ *
+ * This function is the same as module_unload(), except that the
+ * module's destructors are not executed.
+ */
+extern int _module_unload(struct elf_module *module);
+
+/**
+ * module_unload - unloads the module from the system.
+ * @module: the module descriptor structure.
+ *
+ * This function returns the type of module we're dealing with
+ * either a library module ( LIB_MODULE ), executable module ( EXEC_MODULE ),
+ * or an error ( UNKNOWN_MODULE ). The way it checks teh type is by checking to see
+ * if the module has its main_func set ( in which case it's an executable ). In case
+ * it doesn't it then checks to see if init_func is set ( in which case it's a
+ * library module. If this isn't the case either we don't know what it is so bail out
+ */
+extern int get_module_type(struct elf_module *module);
+
+/**
+ * module_unloadable - checks whether the given module can be unloaded.
+ * @module: the module descriptor structure
+ *
+ * A module can be unloaded from the system when no other modules depend on it,
+ * that is, no symbols are referenced from it.
+ */
+extern int module_unloadable(struct elf_module *module);
+
+/**
+ * module_find - searches for a module by its name.
+ * @name: the name of the module, as it was specified in module_alloc.
+ *
+ * The function returns a pointer to the module descriptor, if found, or
+ * NULL otherwise.
+ */
+extern struct elf_module *module_find(const char *name);
+
+/**
+ * module_find_symbol - searches for a symbol definition in a given module.
+ * @name: the name of the symbol to be found.
+ * @module: the module descriptor structure.
+ *
+ * The function searches the module symbol table for a symbol matching exactly
+ * the name provided. The operation uses the following search algorithms, in this
+ * order:
+ * - If a GNU hash table is present in the module, it is used to find the symbol.
+ * - If the symbol cannot be found with the first method (either the hash table
+ * is not present or the symbol is not found) and if a regular (SysV) hash table
+ * is present, a search is performed on the SysV hash table. If the symbol is not
+ * found, NULL is returned.
+ * - If the second method cannot be applied, a linear search is performed by
+ * inspecting every symbol in the symbol table.
+ *
+ * If the symbol is found, a pointer to its descriptor structure is returned, and
+ * NULL otherwise.
+ */
+extern Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module);
+
+/**
+ * global_find_symbol - searches for a symbol definition in the entire module namespace.
+ * @name: the name of the symbol to be found.
+ * @module: an optional (may be NULL) pointer to a module descriptor variable that
+ * will hold the module where the symbol was found.
+ *
+ * The function search for the given symbol name in all the modules currently
+ * loaded in the system, in the reverse module loading order. That is, the most
+ * recently loaded module is searched first, followed by the previous one, until
+ * the first loaded module is reached.
+ *
+ * If no module contains the symbol, NULL is returned, otherwise the return value is
+ * a pointer to the symbol descriptor structure. If the module parameter is not NULL,
+ * it is filled with the address of the module descriptor where the symbol is defined.
+ */
+extern Elf32_Sym *global_find_symbol(const char *name, struct elf_module **module);
+
+/**
+ * module_get_absolute - converts an memory address relative to a module base address
+ * to its absolute value in RAM.
+ * @addr: the relative address to convert.
+ * @module: the module whose base address is used for the conversion.
+ *
+ * The function returns a pointer to the absolute memory address.
+ */
+static inline void *module_get_absolute(Elf32_Addr addr, struct elf_module *module) {
+ return (void*)(module->base_addr + addr);
+}
+
+/**
+ * syslinux_current - get the current module process
+ */
+extern struct elf_module *__syslinux_current;
+static inline const struct elf_module *syslinux_current(void)
+{
+ return __syslinux_current;
+}
+
+
+#endif // DYNAMIC_MODULE
+
+#endif // MODULE_H_
diff --git a/com32/include/sys/times.h b/com32/include/sys/times.h
index 90470063..5eda2954 100644
--- a/com32/include/sys/times.h
+++ b/com32/include/sys/times.h
@@ -16,6 +16,12 @@ struct tms {
typedef uint32_t clock_t;
-clock_t times(struct tms *);
+extern volatile uint32_t __ms_timer;
+
+static inline clock_t times(struct tms *buf)
+{
+ (void)buf;
+ return __ms_timer;
+}
#endif /* _SYS_TIMES_H */
diff --git a/com32/include/syslinux/boot.h b/com32/include/syslinux/boot.h
index 21bea01a..74a311df 100644
--- a/com32/include/syslinux/boot.h
+++ b/com32/include/syslinux/boot.h
@@ -40,7 +40,7 @@
int syslinux_run_command(const char *);
__noreturn syslinux_run_default(void);
-void syslinux_local_boot(uint16_t flags);
+void syslinux_local_boot(int16_t flags);
void syslinux_final_cleanup(uint16_t flags);
@@ -48,15 +48,24 @@ void syslinux_chain_bootstrap(uint16_t flags, const void *bootstrap,
uint32_t bootstrap_len, uint32_t edx,
uint32_t esi, uint16_t ds);
+struct image_types {
+ const char *name;
+ uint32_t type;
+};
+
+extern const struct image_types image_boot_types[];
+
#define IMAGE_TYPE_KERNEL 0
#define IMAGE_TYPE_LINUX 1
#define IMAGE_TYPE_BOOT 2
#define IMAGE_TYPE_BSS 3
#define IMAGE_TYPE_PXE 4
#define IMAGE_TYPE_FDIMAGE 5
-#define IMAGE_TYPE_COMBOOT 6
#define IMAGE_TYPE_COM32 7
#define IMAGE_TYPE_CONFIG 8
+#define IMAGE_TYPE_LOCALBOOT 9
+
+uint32_t parse_image_type(const char *cmdline);
void syslinux_run_kernel_image(const char *filename, const char *cmdline,
uint32_t ipappend_flags, uint32_t type);
diff --git a/com32/include/syslinux/config.h b/com32/include/syslinux/config.h
index 79a4750e..235f288d 100644
--- a/com32/include/syslinux/config.h
+++ b/com32/include/syslinux/config.h
@@ -39,7 +39,7 @@
#include <com32.h>
enum syslinux_filesystem {
- SYSLINUX_FS_UNKNOWN = 0x30,
+ SYSLINUX_FS_UNKNOWN = 0x30,
SYSLINUX_FS_SYSLINUX = 0x31,
SYSLINUX_FS_PXELINUX = 0x32,
SYSLINUX_FS_ISOLINUX = 0x33,
@@ -156,6 +156,8 @@ struct syslinux_serial_console_info {
uint16_t flowctl;
};
+extern void __syslinux_set_serial_console_info(void);
+
extern __nocommon struct syslinux_serial_console_info
__syslinux_serial_console_info;
static inline const struct syslinux_serial_console_info
@@ -164,10 +166,10 @@ static inline const struct syslinux_serial_console_info
return &__syslinux_serial_console_info;
}
-extern __nocommon const char *__syslinux_config_file;
+extern char ConfigName[];
static inline const char *syslinux_config_file(void)
{
- return __syslinux_config_file;
+ return ConfigName;
}
struct syslinux_ipappend_strings {
@@ -181,4 +183,9 @@ static inline const struct syslinux_ipappend_strings
return &__syslinux_ipappend_strings;
}
+static inline enum syslinux_filesystem syslinux_filesystem(void)
+{
+ return syslinux_derivative_info()->c.filesystem;
+}
+
#endif /* _SYSLINUX_CONFIG_H */
diff --git a/com32/include/syslinux/debug.h b/com32/include/syslinux/debug.h
new file mode 100644
index 00000000..aee6fdbb
--- /dev/null
+++ b/com32/include/syslinux/debug.h
@@ -0,0 +1,15 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdbool.h>
+
+#ifdef DYNAMIC_DEBUG
+#define syslinux_debug_enabled __syslinux_debug_enabled(__func__)
+extern bool __syslinux_debug_enabled(const char *func);
+#else
+#define syslinux_debug_enabled (0)
+#endif /* DYNAMIC_DEBUG */
+
+extern int syslinux_debug(const char *str, bool enable);
+
+#endif /* DEBUG_H */
diff --git a/com32/include/syslinux/disk.h b/com32/include/syslinux/disk.h
index f96ca686..b8361fe4 100644
--- a/com32/include/syslinux/disk.h
+++ b/com32/include/syslinux/disk.h
@@ -41,6 +41,13 @@
#define SECTOR 512u /* bytes/sector */
+enum disk_op_codes {
+ EBIOS_READ_CODE = 0x42, /* Extended read */
+ EBIOS_WRITE_CODE = 0x43, /* Extended write */
+ CHS_READ_CODE = 0x02,
+ CHS_WRITE_CODE = 0x03,
+};
+
struct disk_info {
int disk;
int ebios; /* EBIOS supported on this disk */
diff --git a/com32/include/syslinux/features.h b/com32/include/syslinux/features.h
deleted file mode 100644
index 4bebda49..00000000
--- a/com32/include/syslinux/features.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall
- * be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * ----------------------------------------------------------------------- */
-
-#ifndef _SYSLINUX_FEATURES_H
-#define _SYSLINUX_FEATURES_H
-
-#define SYSLINUX_FEATURE_LOCAL_BOOT (0*8+0)
-#define SYSLINUX_FEATURE_NOOP_IDLE (0*8+1)
-
-extern struct __syslinux_feature_flags {
- unsigned int len;
- const unsigned char *ptr;
-} __syslinux_feature_flags;
-
-static inline int syslinux_has_feature(unsigned int __flag)
-{
- unsigned int __byte = __flag >> 3;
- unsigned int __bit = __flag & 7;
-
- if (__byte <= __syslinux_feature_flags.len)
- return (__syslinux_feature_flags.ptr[__byte] >> __bit) & 1;
- else
- return 0;
-}
-
-#endif /* _SYSLINUX_FEATURE_H */
diff --git a/com32/include/syslinux/pmapi.h b/com32/include/syslinux/pmapi.h
index fa390185..14a2c326 100644
--- a/com32/include/syslinux/pmapi.h
+++ b/com32/include/syslinux/pmapi.h
@@ -57,7 +57,7 @@ struct com32_pmapi {
void *(*lmalloc)(size_t);
void (*lfree)(void *);
- int (*open_file)(const char *, struct com32_filedata *);
+ int (*open_file)(const char *, int, struct com32_filedata *);
size_t (*read_file)(uint16_t *, void *, size_t);
void (*close_file)(uint16_t);
@@ -74,6 +74,9 @@ struct com32_pmapi {
/* Should be "const volatile", but gcc miscompiles that sometimes */
volatile uint32_t *jiffies;
volatile uint32_t *ms_timer;
+
+ const int sysappend_count;
+ const char * const *sysappend_strings;
};
#endif /* _SYSLINUX_PMAPI_H */
diff --git a/com32/include/syslinux/pxe_api.h b/com32/include/syslinux/pxe_api.h
index 27166b0b..e9baa48c 100644
--- a/com32/include/syslinux/pxe_api.h
+++ b/com32/include/syslinux/pxe_api.h
@@ -359,7 +359,24 @@ typedef struct s_PXENV_UNDI_GET_IFACE_INFO {
uint32_t LinkSpeed;
uint32_t ServiceFlags;
uint32_t Reserved[4];
-} __packed t_PXENV_UNDI_GET_NDIS_INFO;
+} __packed t_PXENV_UNDI_GET_IFACE_INFO;
+#define PXE_UNDI_IFACE_FLAG_BCAST 0x00000001
+#define PXE_UNDI_IFACE_FLAG_MCAST 0x00000002
+#define PXE_UNDI_IFACE_FLAG_GROUP 0x00000004
+#define PXE_UNDI_IFACE_FLAG_PROMISC 0x00000008
+#define PXE_UNDI_IFACE_FLAG_SOFTMAC 0x00000010
+#define PXE_UNDI_IFACE_FLAG_STATS 0x00000020
+#define PXE_UNDI_IFACE_FLAG_DIAGS 0x00000040
+#define PXE_UNDI_IFACE_FLAG_LOOPBACK 0x00000080
+#define PXE_UNDI_IFACE_FLAG_RCVCHAIN 0x00000100
+#define PXE_UNDI_IFACE_FLAG_IBMSRCRT 0x00000200
+#define PXE_UNDI_IFACE_FLAG_RESET 0x00000400
+#define PXE_UNDI_IFACE_FLAG_OPEN 0x00000800
+#define PXE_UNDI_IFACE_FLAG_IRQ 0x00001000
+#define PXE_UNDI_IFACE_FLAG_SRCRT 0x00002000
+#define PXE_UNDI_IFACE_FLAG_GDTVIRT 0x00004000
+#define PXE_UNDI_IFACE_FLAG_MULTI 0x00008000
+#define PXE_UNDI_IFACE_FLAG_LKFISZ 0x00010000
typedef struct s_PXENV_UNDI_GET_STATE {
#define PXE_UNDI_GET_STATE_STARTED 1
@@ -568,4 +585,11 @@ typedef struct s_PXENV_UNLOAD_STACK {
#define PXENV_STATUS_LOADER_UNDI_START 0xca
#define PXENV_STATUS_LOADER_BC_START 0xcb
+int __weak pxe_call(int, void *);
+void __weak unload_pxe(uint16_t flags);
+uint32_t __weak dns_resolv(const char *);
+
+uint32_t __weak SendCookies;
+void __weak http_bake_cookies(void);
+
#endif /* _SYSLINUX_PXE_API_H */
diff --git a/com32/lib/syslinux/video/forcetext.c b/com32/include/syslinux/sysappend.h
index 136cb279..f243eabc 100644
--- a/com32/lib/syslinux/video/forcetext.c
+++ b/com32/include/syslinux/sysappend.h
@@ -1,7 +1,6 @@
/* ----------------------------------------------------------------------- *
*
- * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
- * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -27,16 +26,34 @@
* ----------------------------------------------------------------------- */
/*
- * syslinux/video/forcetext.c
+ * syslinux/sysappend.h
+ *
+ * List of the Syslinux sysappend strings
*/
-#include <syslinux/video.h>
-#include <com32.h>
+#ifndef _SYSLINUX_SYSAPPEND_H
+#define _SYSLINUX_SYSAPPEND_H
-void syslinux_force_text_mode(void)
-{
- static com32sys_t ireg;
+enum syslinux_sysappend {
+ SYSAPPEND_IP, /* PXELINUX: ip= address */
+ SYSAPPEND_BOOTIF, /* PXELINUX: BOOTIF= address */
+ SYSAPPEND_SYSUUID, /* System UUID from PXE or DMI */
+ SYSAPPEND_CPU, /* CPU features */
+ SYSAPPEND_SYSVENDOR, /* System or MB vendor from DMI */
+ SYSAPPEND_SYSPRODUCT, /* System or MB product from DMI */
+ SYSAPPEND_SYSVERSION, /* System or MB version from DMI */
+ SYSAPPEND_SYSSERIAL, /* System or MB serial from DMI */
+ SYSAPPEND_SYSSKU, /* System SKU from DMI */
+ SYSAPPEND_SYSFAMILY, /* System family from DMI */
+ SYSAPPEND_MBVENDOR, /* System or MB vendor from DMI */
+ SYSAPPEND_MBPRODUCT, /* System or MB product from DMI */
+ SYSAPPEND_MBVERSION, /* System or MB version from DMI */
+ SYSAPPEND_MBSERIAL, /* System or MB serial from DMI */
+ SYSAPPEND_MBASSET, /* MB asset tag from DMI */
+ SYSAPPEND_BIOSVENDOR, /* BIOS vendor */
+ SYSAPPEND_BIOSVERSION, /* BIOS version string */
+ SYSAPPEND_SYSFF, /* System form factor */
+ SYSAPPEND_MAX /* Total number of strings */
+};
- ireg.eax.w[0] = 0x0005;
- __intcall(0x22, &ireg, NULL);
-}
+#endif
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index 5ab1fac4..d1b6bf7f 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -8,149 +8,203 @@ topdir = ../../
MAKEDIR = $(topdir)/mk
include $(MAKEDIR)/lib.mk
-LIBOBJS = \
- abort.o atexit.o atoi.o atol.o atoll.o calloc.o creat.o \
- ctypes.o errno.o fgetc.o fgets.o fopen.o fprintf.o fputc.o \
- fclose.o putchar.o setjmp.o \
- fputs.o fread2.o fread.o free.o fwrite2.o fwrite.o \
- getopt.o getopt_long.o \
- lrand48.o malloc.o stack.o memccpy.o memchr.o memcmp.o \
- memcpy.o mempcpy.o memmem.o memmove.o memset.o memswap.o \
- exit.o onexit.o \
- perror.o printf.o puts.o qsort.o realloc.o seed48.o snprintf.o \
- sprintf.o srand48.o sscanf.o stack.o strcasecmp.o strcat.o \
- strchr.o strcmp.o strcpy.o strdup.o strlen.o \
- strerror.o strnlen.o \
- strncasecmp.o strncat.o strncmp.o strncpy.o strndup.o \
- stpcpy.o stpncpy.o \
- strntoimax.o strntoumax.o strrchr.o strsep.o strspn.o strstr.o \
- strtoimax.o strtok.o strtol.o strtoll.o strtoul.o strtoull.o \
- strtoumax.o vfprintf.o vprintf.o vsnprintf.o vsprintf.o \
- asprintf.o vasprintf.o strlcpy.o strlcat.o \
- vsscanf.o zalloc.o \
- skipspace.o \
- chrreplace.o \
- bufprintf.o \
- inet.o dhcppack.o dhcpunpack.o \
- strreplace.o \
- \
- lmalloc.o lstrdup.o \
- \
- dprintf.o vdprintf.o \
- \
- suffix_number.o \
+## OPTIONAL OBJECTS, AVAILABLE AS DYNAMIC LINKED MODULES
+# PNG library object files
+LIBPNG_OBJS = \
+ libpng/png.o libpng/pngset.o libpng/pngget.o libpng/pngrutil.o \
+ libpng/pngtrans.o libpng/pngwutil.o libpng/pngread.o \
+ libpng/pngrio.o libpng/pngwio.o libpng/pngwrite.o \
+ libpng/pngrtran.o libpng/pngwtran.o libpng/pngmem.o \
+ libpng/pngerror.o libpng/pngpread.o
+
+# ZIP library object files
+LIBZLIB_OBJS = \
+ zlib/adler32.o zlib/compress.o zlib/crc32.o \
+ zlib/uncompr.o zlib/deflate.o zlib/trees.o zlib/zutil.o \
+ zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o \
+ sys/zfile.o sys/zfopen.o
+
+# JPG library object files
+LIBJPG_OBJS = \
+ jpeg/tinyjpeg.o jpeg/jidctflt.o jpeg/decode1.o jpeg/decode3.o \
+ jpeg/rgb24.o jpeg/bgr24.o jpeg/yuv420p.o jpeg/grey.o \
+ jpeg/rgba32.o jpeg/bgra32.o
+
+LIBVESA_OBJS = \
+ sys/vesacon_write.o sys/vesaserial_write.o \
+ sys/vesa/initvesa.o sys/vesa/drawtxt.o sys/vesa/background.o \
+ sys/vesa/alphatbl.o sys/vesa/screencpy.o sys/vesa/fmtpixel.o \
+ sys/vesa/i915resolution.o
+
+LIBMISC_OBJS = \
+ sys/libansi.o sys/gpxe.o
+
+LIBPCI_OBJS = \
+ pci/cfgtype.o pci/scan.o pci/bios.o \
+ pci/readb.o pci/readw.o pci/readl.o \
+ pci/writeb.o pci/writew.o pci/writel.o
+
+LIBSYSLINUX_OBJS = \
+ syslinux/reboot.o syslinux/keyboard.o \
+ syslinux/version.o \
+ syslinux/pxe_get_cached.o syslinux/pxe_get_nic.o \
+ syslinux/pxe_dns.o \
+ syslinux/video/fontquery.o syslinux/video/reportmode.o
+
+LIBLOAD_OBJS = \
+ syslinux/addlist.o syslinux/freelist.o syslinux/memmap.o \
+ syslinux/movebits.o syslinux/shuffle.o syslinux/shuffle_pm.o \
+ syslinux/shuffle_rm.o syslinux/zonelist.o \
+ syslinux/dump_mmap.o syslinux/dump_movelist.o \
\
- sys/readdir.o getcwd.o chdir.o fdopendir.o \
+ syslinux/run_default.o syslinux/run_command.o \
+ syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o \
\
- libgcc/__ashldi3.o libgcc/__udivdi3.o \
- libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o \
- libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o \
- libgcc/__divdi3.o libgcc/__moddi3.o \
+ syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o \
\
+ syslinux/load_linux.o syslinux/initramfs.o \
+ syslinux/initramfs_file.o syslinux/initramfs_loadfile.o \
+ syslinux/initramfs_archive.o
+
+DYNENTRY_OBJS = \
+ atexit.o onexit.o abort.o
+
+## CORE OBJECTS, INCLUDED IN THE ROOT COM32 MODULE
+LIBENTRY_OBJS = \
sys/intcall.o sys/farcall.o sys/cfarcall.o sys/zeroregs.o \
- sys/entry.o sys/exit.o sys/argv.o sys/times.o sys/sleep.o \
+ sys/argv.o sys/sleep.o \
sys/fileinfo.o sys/opendev.o sys/read.o sys/write.o sys/ftell.o \
sys/close.o sys/open.o sys/fileread.o sys/fileclose.o \
- sys/openmem.o \
+ sys/openmem.o \
sys/isatty.o sys/fstat.o \
\
- sys/zfile.o sys/zfopen.o \
+ dprintf.o vdprintf.o \
+ \
+ syslinux/idle.o \
+ \
+ exit.o
+
+LIBMODULE_OBJS = \
+ sys/module/common.o sys/module/elf_module.o \
+ sys/module/elfutils.o \
+ sys/module/exec.o
+
+LIBGCC_OBJS = \
+ libgcc/__ashldi3.o libgcc/__udivdi3.o \
+ libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o \
+ libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o \
+ libgcc/__divdi3.o libgcc/__moddi3.o
+
+LIBCONSOLE_OBJS = \
\
sys/openconsole.o sys/line_input.o \
sys/colortable.o sys/screensize.o \
\
- sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o \
- sys/rawcon_write.o sys/err_read.o sys/err_write.o \
- sys/null_read.o sys/null_write.o sys/serial_write.o \
+ sys/stdcon_read.o sys/rawcon_read.o \
+ sys/rawcon_write.o \
+ sys/null_write.o sys/serial_write.o \
\
sys/xserial_write.o \
\
sys/ansi.o \
\
- sys/libansi.o \
- \
- sys/gpxe.o \
+ sys/ansicon_write.o sys/ansiserial_write.o \
\
- sys/ansicon_write.o sys/ansiserial_write.o \
- \
- sys/vesacon_write.o sys/vesaserial_write.o \
- sys/vesa/initvesa.o sys/vesa/drawtxt.o sys/vesa/background.o \
- sys/vesa/alphatbl.o sys/vesa/screencpy.o sys/vesa/fmtpixel.o \
- sys/vesa/i915resolution.o \
- \
- pci/cfgtype.o pci/scan.o pci/bios.o \
- pci/readb.o pci/readw.o pci/readl.o \
- pci/writeb.o pci/writew.o pci/writel.o \
- \
- zlib/adler32.o zlib/compress.o zlib/crc32.o \
- zlib/uncompr.o zlib/deflate.o zlib/trees.o zlib/zutil.o \
- zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o \
- \
- libpng/png.o libpng/pngset.o libpng/pngget.o libpng/pngrutil.o \
- libpng/pngtrans.o libpng/pngwutil.o libpng/pngread.o \
- libpng/pngrio.o libpng/pngwio.o libpng/pngwrite.o \
- libpng/pngrtran.o libpng/pngwtran.o libpng/pngmem.o \
- libpng/pngerror.o libpng/pngpread.o \
- \
- jpeg/tinyjpeg.o jpeg/jidctflt.o jpeg/decode1.o jpeg/decode3.o \
- jpeg/grey.o jpeg/yuv420p.o \
- jpeg/rgb24.o jpeg/bgr24.o \
- jpeg/rgba32.o jpeg/bgra32.o \
- \
- sys/x86_init_fpu.o math/pow.o math/strtod.o \
- \
- syslinux/idle.o syslinux/reboot.o \
- syslinux/features.o syslinux/config.o syslinux/serial.o \
- syslinux/ipappend.o syslinux/dsinfo.o syslinux/version.o \
- syslinux/keyboard.o \
+ syslinux/serial.o
+
+LIBOTHER_OBJS = \
+ atoi.o atol.o atoll.o calloc.o creat.o \
+ fgets.o fprintf.o fputc.o \
+ putchar.o \
+ getopt.o getopt_long.o \
+ lrand48.o stack.o memccpy.o memchr.o \
+ mempcpy.o memmem.o memmove.o memswap.o \
+ perror.o qsort.o seed48.o \
+ srand48.o sscanf.o strcasecmp.o \
+ strerror.o errlist.o \
+ strnlen.o \
+ strncat.o strndup.o \
+ stpncpy.o \
+ strntoimax.o strsep.o strspn.o strstr.o \
+ strtoimax.o strtok.o strtol.o strtoll.o strtoull.o \
+ strtoumax.o vprintf.o vsprintf.o \
+ asprintf.o vasprintf.o \
+ vsscanf.o \
+ skipspace.o \
+ chrreplace.o \
+ bufprintf.o \
+ inet.o dhcppack.o dhcpunpack.o \
+ strreplace.o \
+ lstrdup.o \
\
- syslinux/memscan.o \
+ suffix_number.o \
\
- syslinux/addlist.o syslinux/freelist.o syslinux/memmap.o \
- syslinux/movebits.o syslinux/shuffle.o syslinux/shuffle_pm.o \
- syslinux/shuffle_rm.o syslinux/zonelist.o \
- syslinux/dump_mmap.o syslinux/dump_movelist.o \
+ getcwd.o fdopendir.o \
\
- syslinux/run_default.o syslinux/run_command.o \
- syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o \
+ sys/line_input.o \
+ sys/colortable.o sys/screensize.o \
\
- syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o \
+ sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o \
+ sys/rawcon_write.o \
+ sys/null_read.o sys/null_write.o sys/serial_write.o \
\
- syslinux/load_linux.o syslinux/initramfs.o \
- syslinux/initramfs_file.o syslinux/initramfs_loadfile.o \
- syslinux/initramfs_archive.o \
+ sys/xserial_write.o \
\
- syslinux/pxe_get_cached.o syslinux/pxe_get_nic.o \
- syslinux/pxe_dns.o \
+ sys/ansi.o \
\
- syslinux/adv.o syslinux/advwrite.o syslinux/getadv.o \
- syslinux/setadv.o \
+ sys/ansicon_write.o sys/ansiserial_write.o \
\
- syslinux/video/fontquery.o syslinux/video/forcetext.o \
- syslinux/video/reportmode.o \
+ pci/cfgtype.o pci/scan.o pci/bios.o \
+ pci/readb.o pci/readw.o pci/readl.o \
+ pci/writeb.o pci/writew.o pci/writel.o \
\
+ sys/x86_init_fpu.o math/pow.o math/strtod.o \
syslinux/disk.o \
\
syslinux/setup_data.o
-# These are the objects which are also imported into the core
-LIBCOREOBJS = \
- memcpy.o mempcpy.o memset.o memcmp.o memmove.o \
- strlen.o stpcpy.o strcpy.o strcmp.o strlcpy.o strlcat.o \
- strchr.o strncmp.o strncpy.o \
- \
- snprintf.o sprintf.o vsnprintf.o \
- \
- dprintf.o vdprintf.o \
- \
- zalloc.o strdup.o \
- \
- sys/intcall.o sys/farcall.o sys/cfarcall.o sys/zeroregs.o \
- \
+CORELIBOBJS = \
+ memcpy.o memset.o memcmp.o printf.o strncmp.o vfprintf.o \
+ strlen.o vsnprintf.o snprintf.o stpcpy.o strcmp.o strdup.o \
+ strcpy.o strncpy.o setjmp.o fopen.o fread.o fread2.o puts.o \
+ sprintf.o strlcat.o strchr.o strlcpy.o strncasecmp.o ctypes.o \
+ fputs.o fwrite2.o fwrite.o fgetc.o fclose.o lmalloc.o strtoul.o \
+ sys/err_read.o sys/err_write.o sys/null_read.o strntoumax.o \
+ sys/stdcon_write.o \
+ syslinux/memscan.o strrchr.o strcat.o \
libgcc/__ashldi3.o libgcc/__udivdi3.o \
libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o \
libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o \
- libgcc/__divdi3.o libgcc/__moddi3.o
+ libgcc/__divdi3.o libgcc/__moddi3.o \
+ syslinux/debug.o \
+ $(LIBENTRY_OBJS) \
+ $(LIBMODULE_OBJS)
+
+MINLIBOBJS = \
+ syslinux/ipappend.o \
+ syslinux/dsinfo.o \
+ $(LIBOTHER_OBJS) \
+ $(LIBGCC_OBJS) \
+ $(LIBCONSOLE_OBJS) \
+ $(LIBLOAD_OBJS) \
+ $(LIBZLIB_OBJS)
+# $(LIBVESA_OBJS)
+
+
+DYNLIBOBJS = \
+ $(LIBZLIB_OBJS) \
+ $(LIBPNG_OBJS) \
+ $(LIBJPG_OBJS) \
+ $(LIBPCI_OBJS) \
+ $(LIBVESA_OBJS) \
+ $(LIBSYSLINUX_OBJS) \
+ $(LIBLOAD_OBJS) \
+ $(LIBMISC_OBJS) \
+ $(DYNENTRY_OBJS)
+
+
+LIBOBJS = \
+ $(DYNLIBOBJS)
BINDIR = /usr/bin
LIBDIR = /usr/lib
@@ -159,33 +213,40 @@ AUXDIR = $(DATADIR)/syslinux
INCDIR = /usr/include
COM32DIR = $(AUXDIR)/com32
-all: libcom32.a libcomcore.a
+all: libcom32.c32 libcom32min.a libcom32core.a
-libcom32.a : $(LIBOBJS)
+libcom32.elf : $(LIBOBJS)
+ rm -f $@
+ $(LD) -shared $(LDFLAGS) -soname $(patsubst %.elf,%.c32,$(@F)) -o $@ $^
+
+libcom32min.a : $(MINLIBOBJS)
rm -f $@
$(AR) cq $@ $^
$(RANLIB) $@
-libcomcore.a : $(LIBCOREOBJS)
+libcom32core.a : $(CORELIBOBJS)
rm -f $@
$(AR) cq $@ $^
$(RANLIB) $@
tidy dist clean:
- rm -f sys/vesa/alphatbl.c
+ rm -f sys/vesa/alphatbl.c errlist.c
find . \( -name \*.o -o -name \*.a -o -name .\*.d -o -name \*.tmp \) -print0 | \
xargs -0r rm -f
spotless: clean
- rm -f *.a
+ rm -f *.a *.c32
rm -f *~ \#* */*~ */\#*
install: all
mkdir -m 755 -p $(INSTALLROOT)$(COM32DIR)
- install -m 644 libcom32.a com32.ld $(INSTALLROOT)$(COM32DIR)
+ install -m 644 com32.ld $(INSTALLROOT)$(COM32DIR)
-rm -rf $(INSTALLROOT)$(COM32DIR)/include
cp -r ../include $(INSTALLROOT)$(COM32DIR)
+errlist.c: makeerrlist.pl ../include/errno.h
+ $(PERL) $< $(CFLAGS) -errlist > $@ || rm -f $@
+
# These files are performance critical, and doesn't compile well with -Os
sys/vesa/drawtxt.o: sys/vesa/drawtxt.c
$(CC) $(MAKEDEPS) $(CFLAGS) -O3 -c -o $@ $<
diff --git a/com32/lib/asprintf.c b/com32/lib/asprintf.c
index ef5b4b2f..eab20118 100644
--- a/com32/lib/asprintf.c
+++ b/com32/lib/asprintf.c
@@ -21,9 +21,10 @@ int asprintf(char **bufp, const char *format, ...)
*bufp = p = malloc(bytes);
if (!p)
- return -1;
+ rv = -1;
+ else
+ rv = vsnprintf(p, bytes, format, ap);
- rv = vsnprintf(p, bytes, format, ap);
va_end(ap);
return rv;
diff --git a/com32/lib/bufprintf.c b/com32/lib/bufprintf.c
index 939bcec3..d2812311 100644
--- a/com32/lib/bufprintf.c
+++ b/com32/lib/bufprintf.c
@@ -17,8 +17,10 @@ int vbufprintf(struct print_buf *buf, const char *format, va_list ap)
char *newbuf;
newbuf = realloc(buf->buf, newsize);
- if (!newbuf)
- return -1;
+ if (!newbuf) {
+ rv = -1;
+ goto bail;
+ }
buf->buf = newbuf;
buf->size = newsize;
@@ -26,6 +28,8 @@ int vbufprintf(struct print_buf *buf, const char *format, va_list ap)
rv = vsnprintf(buf->buf + buf->len, buf->size - buf->len, format, ap2);
buf->len += rv;
+bail:
+ va_end(ap2);
return rv;
}
diff --git a/com32/lib/chdir.c b/com32/lib/chdir.c
deleted file mode 100644
index 00670e35..00000000
--- a/com32/lib/chdir.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * chdir.c
- */
-
-#include <dirent.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <com32.h>
-#include <syslinux/pmapi.h>
-
-int chdir(const char *path)
-{
- return __com32.cs_pm->chdir(path);
-}
diff --git a/com32/lib/dprintf.c b/com32/lib/dprintf.c
index aad11746..dea77b39 100644
--- a/com32/lib/dprintf.c
+++ b/com32/lib/dprintf.c
@@ -5,11 +5,10 @@
#include <stdio.h>
#include <stdarg.h>
-#undef DEBUG
-#define DEBUG 1
-#include <dprintf.h>
+#ifdef DEBUG_PORT
+
+void vdprintf(const char *, va_list);
-#ifndef dprintf
void dprintf(const char *format, ...)
{
va_list ap;
@@ -18,4 +17,5 @@ void dprintf(const char *format, ...)
vdprintf(format, ap);
va_end(ap);
}
-#endif
+
+#endif /* DEBUG_PORT */
diff --git a/com32/lib/elf32.ld b/com32/lib/elf32.ld
new file mode 100644
index 00000000..16d10a38
--- /dev/null
+++ b/com32/lib/elf32.ld
@@ -0,0 +1,171 @@
+/*
+ * Linker script for ELF dynamic loaded modules.
+ */
+
+/* Script for --shared -z combreloc: shared library, combine & sort relocs */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+ "elf32-i386")
+OUTPUT_ARCH(i386)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0 + SIZEOF_HEADERS;
+ .note.gnu.build-id : { *(.note.gnu.build-id) }
+ .hash : { *(.hash) }
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x90909090
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x90909090
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x90909090
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ . = ALIGN(4);
+ .preinit_array :
+ {
+ KEEP (*(.preinit_array))
+ }
+ .ctors :
+ {
+ __ctors_start = .;
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ KEEP (*(.ctors_modinit))
+ KEEP (*(.ctors_modmain))
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ __ctors_end = .;
+ }
+
+ .dtors :
+ {
+ __dtors_start = .;
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ KEEP (*(.dtors_modexit))
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ __dtors_end = .;
+ }
+
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+ .dynamic : { *(.dynamic) }
+ .got : { *(.got) }
+ /*. = DATA_SEGMENT_RELRO_END (12, .); -> This gives a "invalid assignment to location counter" error */
+ .got.plt : { *(.got.plt) }
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ PROVIDE (edata = .);
+ PROVIDE (_edata = .);
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections.
+ FIXME: Why do we need it? When there is no .bss section, we don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ . = ALIGN(32 / 8);
+ . = ALIGN(32 / 8);
+ PROVIDE (_end = .);
+ PROVIDE (end = .);
+ /*. = DATA_SEGMENT_END (.); -> This gives a "invalid assignment to location counter" error */
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+ /DISCARD/ : { *(.eh_frame) }
+}
diff --git a/com32/lib/errno.c b/com32/lib/errno.c
deleted file mode 100644
index f280e309..00000000
--- a/com32/lib/errno.c
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * errno.c
- *
- */
-#include <errno.h>
-
-int errno;
diff --git a/com32/lib/exit.c b/com32/lib/exit.c
index ccd6f1ee..ebec0a1a 100644
--- a/com32/lib/exit.c
+++ b/com32/lib/exit.c
@@ -31,11 +31,29 @@
* The regular exit
*/
+#include <sys/module.h>
#include <stdlib.h>
-
-extern __noreturn(*__exit_handler) (int);
+#include <unistd.h>
+#include "atexit.h"
__noreturn exit(int rv)
{
- __exit_handler(rv);
+ struct atexit *ap;
+
+ for (ap = __syslinux_current->u.x.atexit_list; ap; ap = ap->next) {
+ ap->fctn(rv, ap->arg); /* This assumes extra args are harmless */
+ }
+
+ _exit(rv);
+}
+
+__noreturn _Exit(int rv)
+{
+ _exit(rv);
}
+
+__noreturn _exit(int rv)
+{
+ longjmp(__syslinux_current->u.x.process_exit, (uint8_t)rv+1);
+}
+
diff --git a/com32/lib/free.c b/com32/lib/free.c
deleted file mode 100644
index be23865a..00000000
--- a/com32/lib/free.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * free.c
- *
- * Very simple linked-list based malloc()/free().
- */
-
-#include <stdlib.h>
-#include "malloc.h"
-
-static struct free_arena_header *__free_block(struct free_arena_header *ah)
-{
- struct free_arena_header *pah, *nah;
-
- pah = ah->a.prev;
- nah = ah->a.next;
- if (pah->a.type == ARENA_TYPE_FREE &&
- (char *)pah + pah->a.size == (char *)ah) {
- /* Coalesce into the previous block */
- pah->a.size += ah->a.size;
- pah->a.next = nah;
- nah->a.prev = pah;
-
-#ifdef DEBUG_MALLOC
- ah->a.type = ARENA_TYPE_DEAD;
-#endif
-
- ah = pah;
- pah = ah->a.prev;
- } else {
- /* Need to add this block to the free chain */
- ah->a.type = ARENA_TYPE_FREE;
-
- ah->next_free = __malloc_head.next_free;
- ah->prev_free = &__malloc_head;
- __malloc_head.next_free = ah;
- ah->next_free->prev_free = ah;
- }
-
- /* In either of the previous cases, we might be able to merge
- with the subsequent block... */
- if (nah->a.type == ARENA_TYPE_FREE &&
- (char *)ah + ah->a.size == (char *)nah) {
- ah->a.size += nah->a.size;
-
- /* Remove the old block from the chains */
- nah->next_free->prev_free = nah->prev_free;
- nah->prev_free->next_free = nah->next_free;
- ah->a.next = nah->a.next;
- nah->a.next->a.prev = ah;
-
-#ifdef DEBUG_MALLOC
- nah->a.type = ARENA_TYPE_DEAD;
-#endif
- }
-
- /* Return the block that contains the called block */
- return ah;
-}
-
-/*
- * This is used to insert a block which is not previously on the
- * free list. Only the a.size field of the arena header is assumed
- * to be valid.
- */
-void __inject_free_block(struct free_arena_header *ah)
-{
- struct free_arena_header *nah;
- size_t a_end = (size_t) ah + ah->a.size;
- size_t n_end;
-
- for (nah = __malloc_head.a.next; nah->a.type != ARENA_TYPE_HEAD;
- nah = nah->a.next) {
- n_end = (size_t) nah + nah->a.size;
-
- /* Is nah entirely beyond this block? */
- if ((size_t) nah >= a_end)
- break;
-
- /* Is this block entirely beyond nah? */
- if ((size_t) ah >= n_end)
- continue;
-
- /* Otherwise we have some sort of overlap - reject this block */
- return;
- }
-
- /* Now, nah should point to the successor block */
- ah->a.next = nah;
- ah->a.prev = nah->a.prev;
- nah->a.prev = ah;
- ah->a.prev->a.next = ah;
-
- __free_block(ah);
-}
-
-void free(void *ptr)
-{
- struct free_arena_header *ah;
-
- if (!ptr)
- return;
-
- ah = (struct free_arena_header *)
- ((struct arena_header *)ptr - 1);
-
-#ifdef DEBUG_MALLOC
- assert(ah->a.type == ARENA_TYPE_USED);
-#endif
-
- __free_block(ah);
-
- /* Here we could insert code to return memory to the system. */
-}
diff --git a/com32/lib/getcwd.c b/com32/lib/getcwd.c
index 5ce62ec0..d5fa9d7d 100644
--- a/com32/lib/getcwd.c
+++ b/com32/lib/getcwd.c
@@ -4,8 +4,9 @@
#include <com32.h>
#include <syslinux/pmapi.h>
+#include <fs.h>
char *getcwd(char *buf, size_t size)
{
- return __com32.cs_pm->getcwd(buf, size);
+ return core_getcwd(buf, size);
}
diff --git a/com32/lib/init.h b/com32/lib/init.h
deleted file mode 100644
index 2d983427..00000000
--- a/com32/lib/init.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * init.h
- *
- * Magic to set up initializers
- */
-
-#ifndef _INIT_H
-#define _INIT_H 1
-
-#include <inttypes.h>
-
-#define COM32_INIT(x) static const void * const __COM32_INIT \
- __attribute__((section(".init_array"),unused)) = (const void * const)&x
-
-#endif /* _INIT_H */
diff --git a/com32/lib/libpng/libpng.txt b/com32/lib/libpng/libpng.txt
new file mode 100644
index 00000000..9360f33b
--- /dev/null
+++ b/com32/lib/libpng/libpng.txt
@@ -0,0 +1,2959 @@
+libpng.txt - A description on how to use and modify libpng
+
+ libpng version 1.2.8 - December 3, 2004
+ Updated and distributed by Glenn Randers-Pehrson
+ <glennrp at users.sourceforge.net>
+ Copyright (c) 1998-2004 Glenn Randers-Pehrson
+ For conditions of distribution and use, see copyright
+ notice in png.h.
+
+ based on:
+
+ libpng 1.0 beta 6 version 0.96 May 28, 1997
+ Updated and distributed by Andreas Dilger
+ Copyright (c) 1996, 1997 Andreas Dilger
+
+ libpng 1.0 beta 2 - version 0.88 January 26, 1996
+ For conditions of distribution and use, see copyright
+ notice in png.h. Copyright (c) 1995, 1996 Guy Eric
+ Schalnat, Group 42, Inc.
+
+ Updated/rewritten per request in the libpng FAQ
+ Copyright (c) 1995, 1996 Frank J. T. Wojcik
+ December 18, 1995 & January 20, 1996
+
+I. Introduction
+
+This file describes how to use and modify the PNG reference library
+(known as libpng) for your own use. There are five sections to this
+file: introduction, structures, reading, writing, and modification and
+configuration notes for various special platforms. In addition to this
+file, example.c is a good starting point for using the library, as
+it is heavily commented and should include everything most people
+will need. We assume that libpng is already installed; see the
+INSTALL file for instructions on how to install libpng.
+
+Libpng was written as a companion to the PNG specification, as a way
+of reducing the amount of time and effort it takes to support the PNG
+file format in application programs.
+
+The PNG specification (second edition), November 2003, is available as
+a W3C Recommendation and as an ISO Standard (ISO/IEC 15948:2003 (E)) at
+<http://www.w3.org/TR/2003/REC-PNG-20031110/
+The W3C and ISO documents have identical technical content.
+
+The PNG-1.2 specification is available at
+<http://www.libpng.org/pub/png/documents/>
+
+The PNG-1.0 specification is available
+as RFC 2083 <http://www.libpng.org/pub/png/documents/> and as a
+W3C Recommendation <http://www.w3.org/TR/REC.png.html>. Some
+additional chunks are described in the special-purpose public chunks
+documents at <http://www.libpng.org/pub/png/documents/>.
+
+Other information
+about PNG, and the latest version of libpng, can be found at the PNG home
+page, <http://www.libpng.org/pub/png/>.
+
+Most users will not have to modify the library significantly; advanced
+users may want to modify it more. All attempts were made to make it as
+complete as possible, while keeping the code easy to understand.
+Currently, this library only supports C. Support for other languages
+is being considered.
+
+Libpng has been designed to handle multiple sessions at one time,
+to be easily modifiable, to be portable to the vast majority of
+machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy
+to use. The ultimate goal of libpng is to promote the acceptance of
+the PNG file format in whatever way possible. While there is still
+work to be done (see the TODO file), libpng should cover the
+majority of the needs of its users.
+
+Libpng uses zlib for its compression and decompression of PNG files.
+Further information about zlib, and the latest version of zlib, can
+be found at the zlib home page, <http://www.info-zip.org/pub/infozip/zlib/>.
+The zlib compression utility is a general purpose utility that is
+useful for more than PNG files, and can be used without libpng.
+See the documentation delivered with zlib for more details.
+You can usually find the source files for the zlib utility wherever you
+find the libpng source files.
+
+Libpng is thread safe, provided the threads are using different
+instances of the structures. Each thread should have its own
+png_struct and png_info instances, and thus its own image.
+Libpng does not protect itself against two threads using the
+same instance of a structure. Note: thread safety may be defeated
+by use of some of the MMX assembler code in pnggccrd.c, which is only
+compiled when the user defines PNG_THREAD_UNSAFE_OK.
+
+II. Structures
+
+There are two main structures that are important to libpng, png_struct
+and png_info. The first, png_struct, is an internal structure that
+will not, for the most part, be used by a user except as the first
+variable passed to every libpng function call.
+
+The png_info structure is designed to provide information about the
+PNG file. At one time, the fields of png_info were intended to be
+directly accessible to the user. However, this tended to cause problems
+with applications using dynamically loaded libraries, and as a result
+a set of interface functions for png_info (the png_get_*() and png_set_*()
+functions) was developed. The fields of png_info are still available for
+older applications, but it is suggested that applications use the new
+interfaces if at all possible.
+
+Applications that do make direct access to the members of png_struct (except
+for png_ptr->jmpbuf) must be recompiled whenever the library is updated,
+and applications that make direct access to the members of png_info must
+be recompiled if they were compiled or loaded with libpng version 1.0.6,
+in which the members were in a different order. In version 1.0.7, the
+members of the png_info structure reverted to the old order, as they were
+in versions 0.97c through 1.0.5. Starting with version 2.0.0, both
+structures are going to be hidden, and the contents of the structures will
+only be accessible through the png_get/png_set functions.
+
+The png.h header file is an invaluable reference for programming with libpng.
+And while I'm on the topic, make sure you include the libpng header file:
+
+#include <png.h>
+
+III. Reading
+
+We'll now walk you through the possible functions to call when reading
+in a PNG file sequentially, briefly explaining the syntax and purpose
+of each one. See example.c and png.h for more detail. While
+progressive reading is covered in the next section, you will still
+need some of the functions discussed in this section to read a PNG
+file.
+
+Setup
+
+You will want to do the I/O initialization(*) before you get into libpng,
+so if it doesn't work, you don't have much to undo. Of course, you
+will also want to insure that you are, in fact, dealing with a PNG
+file. Libpng provides a simple check to see if a file is a PNG file.
+To use it, pass in the first 1 to 8 bytes of the file to the function
+png_sig_cmp(), and it will return 0 if the bytes match the corresponding
+bytes of the PNG signature, or nonzero otherwise. Of course, the more bytes
+you pass in, the greater the accuracy of the prediction.
+
+If you are intending to keep the file pointer open for use in libpng,
+you must ensure you don't read more than 8 bytes from the beginning
+of the file, and you also have to make a call to png_set_sig_bytes_read()
+with the number of bytes you read from the beginning. Libpng will
+then only check the bytes (if any) that your program didn't read.
+
+(*): If you are not using the standard I/O functions, you will need
+to replace them with custom functions. See the discussion under
+Customizing libpng.
+
+
+ FILE *fp = fopen(file_name, "rb");
+ if (!fp)
+ {
+ return (ERROR);
+ }
+ fread(header, 1, number, fp);
+ is_png = !png_sig_cmp(header, 0, number);
+ if (!is_png)
+ {
+ return (NOT_PNG);
+ }
+
+
+Next, png_struct and png_info need to be allocated and initialized. In
+order to ensure that the size of these structures is correct even with a
+dynamically linked libpng, there are functions to initialize and
+allocate the structures. We also pass the library version, optional
+pointers to error handling functions, and a pointer to a data struct for
+use by the error functions, if necessary (the pointer and functions can
+be NULL if the default error handlers are to be used). See the section
+on Changes to Libpng below regarding the old initialization functions.
+The structure allocation functions quietly return NULL if they fail to
+create the structure, so your application should check for that.
+
+ png_structp png_ptr = png_create_read_struct
+ (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+ user_error_fn, user_warning_fn);
+ if (!png_ptr)
+ return (ERROR);
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct(&png_ptr,
+ (png_infopp)NULL, (png_infopp)NULL);
+ return (ERROR);
+ }
+
+ png_infop end_info = png_create_info_struct(png_ptr);
+ if (!end_info)
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr,
+ (png_infopp)NULL);
+ return (ERROR);
+ }
+
+If you want to use your own memory allocation routines,
+define PNG_USER_MEM_SUPPORTED and use
+png_create_read_struct_2() instead of png_create_read_struct():
+
+ png_structp png_ptr = png_create_read_struct_2
+ (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+ user_error_fn, user_warning_fn, (png_voidp)
+ user_mem_ptr, user_malloc_fn, user_free_fn);
+
+The error handling routines passed to png_create_read_struct()
+and the memory alloc/free routines passed to png_create_struct_2()
+are only necessary if you are not using the libpng supplied error
+handling and memory alloc/free functions.
+
+When libpng encounters an error, it expects to longjmp back
+to your routine. Therefore, you will need to call setjmp and pass
+your png_jmpbuf(png_ptr). If you read the file from different
+routines, you will need to update the jmpbuf field every time you enter
+a new routine that will call a png_*() function.
+
+See your documentation of setjmp/longjmp for your compiler for more
+information on setjmp/longjmp. See the discussion on libpng error
+handling in the Customizing Libpng section below for more information
+on the libpng error handling. If an error occurs, and libpng longjmp's
+back to your setjmp, you will want to call png_destroy_read_struct() to
+free any memory.
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr,
+ &end_info);
+ fclose(fp);
+ return (ERROR);
+ }
+
+If you would rather avoid the complexity of setjmp/longjmp issues,
+you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case
+errors will result in a call to PNG_ABORT() which defaults to abort().
+
+Now you need to set up the input code. The default for libpng is to
+use the C function fread(). If you use this, you will need to pass a
+valid FILE * in the function png_init_io(). Be sure that the file is
+opened in binary mode. If you wish to handle reading data in another
+way, you need not call the png_init_io() function, but you must then
+implement the libpng I/O methods discussed in the Customizing Libpng
+section below.
+
+ png_init_io(png_ptr, fp);
+
+If you had previously opened the file and read any of the signature from
+the beginning in order to see if this was a PNG file, you need to let
+libpng know that there are some bytes missing from the start of the file.
+
+ png_set_sig_bytes(png_ptr, number);
+
+Setting up callback code
+
+You can set up a callback function to handle any unknown chunks in the
+input stream. You must supply the function
+
+ read_chunk_callback(png_ptr ptr,
+ png_unknown_chunkp chunk);
+ {
+ /* The unknown chunk structure contains your
+ chunk data: */
+ png_byte name[5];
+ png_byte *data;
+ png_size_t size;
+ /* Note that libpng has already taken care of
+ the CRC handling */
+
+ /* put your code here. Return one of the
+ following: */
+
+ return (-n); /* chunk had an error */
+ return (0); /* did not recognize */
+ return (n); /* success */
+ }
+
+(You can give your function another name that you like instead of
+"read_chunk_callback")
+
+To inform libpng about your function, use
+
+ png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr,
+ read_chunk_callback);
+
+This names not only the callback function, but also a user pointer that
+you can retrieve with
+
+ png_get_user_chunk_ptr(png_ptr);
+
+At this point, you can set up a callback function that will be
+called after each row has been read, which you can use to control
+a progress meter or the like. It's demonstrated in pngtest.c.
+You must supply a function
+
+ void read_row_callback(png_ptr ptr, png_uint_32 row,
+ int pass);
+ {
+ /* put your code here */
+ }
+
+(You can give it another name that you like instead of "read_row_callback")
+
+To inform libpng about your function, use
+
+ png_set_read_status_fn(png_ptr, read_row_callback);
+
+Width and height limits
+
+The PNG specification allows the width and height of an image to be as
+large as 2^31-1 (0x7fffffff), or about 2.147 billion rows and columns.
+Since very few applications really need to process such large images,
+we have imposed an arbitrary 1-million limit on rows and columns.
+Larger images will be rejected immediately with a png_error() call. If
+you wish to override this limit, you can use
+
+ png_set_user_limits(png_ptr, width_max, height_max);
+
+to set your own limits, or use width_max = height_max = 0x7fffffffL
+to allow all valid dimensions (libpng may reject some very large images
+anyway because of potential buffer overflow conditions).
+
+You should put this statement after you create the PNG structure and
+before calling png_read_info(), png_read_png(), or png_process_data().
+If you need to retrieve the limits that are being applied, use
+
+ width_max = png_get_user_width_max(png_ptr);
+ height_max = png_get_user_height_max(png_ptr);
+
+Unknown-chunk handling
+
+Now you get to set the way the library processes unknown chunks in the
+input PNG stream. Both known and unknown chunks will be read. Normal
+behavior is that known chunks will be parsed into information in
+various info_ptr members; unknown chunks will be discarded. To change
+this, you can call:
+
+ png_set_keep_unknown_chunks(png_ptr, keep,
+ chunk_list, num_chunks);
+ keep - 0: do not handle as unknown
+ 1: do not keep
+ 2: keep only if safe-to-copy
+ 3: keep even if unsafe-to-copy
+ You can use these definitions:
+ PNG_HANDLE_CHUNK_AS_DEFAULT 0
+ PNG_HANDLE_CHUNK_NEVER 1
+ PNG_HANDLE_CHUNK_IF_SAFE 2
+ PNG_HANDLE_CHUNK_ALWAYS 3
+ chunk_list - list of chunks affected (a byte string,
+ five bytes per chunk, NULL or '\0' if
+ num_chunks is 0)
+ num_chunks - number of chunks affected; if 0, all
+ unknown chunks are affected. If nonzero,
+ only the chunks in the list are affected
+
+Unknown chunks declared in this way will be saved as raw data onto a
+list of png_unknown_chunk structures. If a chunk that is normally
+known to libpng is named in the list, it will be handled as unknown,
+according to the "keep" directive. If a chunk is named in successive
+instances of png_set_keep_unknown_chunks(), the final instance will
+take precedence. The IHDR and IEND chunks should not be named in
+chunk_list; if they are, libpng will process them normally anyway.
+
+The high-level read interface
+
+At this point there are two ways to proceed; through the high-level
+read interface, or through a sequence of low-level read operations.
+You can use the high-level interface if (a) you are willing to read
+the entire image into memory, and (b) the input transformations
+you want to do are limited to the following set:
+
+ PNG_TRANSFORM_IDENTITY No transformation
+ PNG_TRANSFORM_STRIP_16 Strip 16-bit samples to
+ 8 bits
+ PNG_TRANSFORM_STRIP_ALPHA Discard the alpha channel
+ PNG_TRANSFORM_PACKING Expand 1, 2 and 4-bit
+ samples to bytes
+ PNG_TRANSFORM_PACKSWAP Change order of packed
+ pixels to LSB first
+ PNG_TRANSFORM_EXPAND Perform set_expand()
+ PNG_TRANSFORM_INVERT_MONO Invert monochrome images
+ PNG_TRANSFORM_SHIFT Normalize pixels to the
+ sBIT depth
+ PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA
+ to BGRA
+ PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA
+ to AG
+ PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity
+ to transparency
+ PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples
+
+(This excludes setting a background color, doing gamma transformation,
+dithering, and setting filler.) If this is the case, simply do this:
+
+ png_read_png(png_ptr, info_ptr, png_transforms, NULL)
+
+where png_transforms is an integer containing the logical OR of
+some set of transformation flags. This call is equivalent to png_read_info(),
+followed the set of transformations indicated by the transform mask,
+then png_read_image(), and finally png_read_end().
+
+(The final parameter of this call is not yet used. Someday it might point
+to transformation parameters required by some future input transform.)
+
+You must use png_transforms and not call any png_set_transform() functions
+when you use png_read_png().
+
+After you have called png_read_png(), you can retrieve the image data
+with
+
+ row_pointers = png_get_rows(png_ptr, info_ptr);
+
+where row_pointers is an array of pointers to the pixel data for each row:
+
+ png_bytep row_pointers[height];
+
+If you know your image size and pixel size ahead of time, you can allocate
+row_pointers prior to calling png_read_png() with
+
+ if (height > PNG_UINT_32_MAX/png_sizeof(png_byte))
+ png_error (png_ptr,
+ "Image is too tall to process in memory");
+ if (width > PNG_UINT_32_MAX/pixel_size)
+ png_error (png_ptr,
+ "Image is too wide to process in memory");
+ row_pointers = png_malloc(png_ptr,
+ height*png_sizeof(png_bytep));
+ for (int i=0; i<height, i++)
+ row_pointers[i]=png_malloc(png_ptr,
+ width*pixel_size);
+ png_set_rows(png_ptr, info_ptr, &row_pointers);
+
+Alternatively you could allocate your image in one big block and define
+row_pointers[i] to point into the proper places in your block.
+
+If you use png_set_rows(), the application is responsible for freeing
+row_pointers (and row_pointers[i], if they were separately allocated).
+
+If you don't allocate row_pointers ahead of time, png_read_png() will
+do it, and it'll be free'ed when you call png_destroy_*().
+
+The low-level read interface
+
+If you are going the low-level route, you are now ready to read all
+the file information up to the actual image data. You do this with a
+call to png_read_info().
+
+ png_read_info(png_ptr, info_ptr);
+
+This will process all chunks up to but not including the image data.
+
+Querying the info structure
+
+Functions are used to get the information from the info_ptr once it
+has been read. Note that these fields may not be completely filled
+in until png_read_end() has read the chunk data following the image.
+
+ png_get_IHDR(png_ptr, info_ptr, &width, &height,
+ &bit_depth, &color_type, &interlace_type,
+ &compression_type, &filter_method);
+
+ width - holds the width of the image
+ in pixels (up to 2^31).
+ height - holds the height of the image
+ in pixels (up to 2^31).
+ bit_depth - holds the bit depth of one of the
+ image channels. (valid values are
+ 1, 2, 4, 8, 16 and depend also on
+ the color_type. See also
+ significant bits (sBIT) below).
+ color_type - describes which color/alpha channels
+ are present.
+ PNG_COLOR_TYPE_GRAY
+ (bit depths 1, 2, 4, 8, 16)
+ PNG_COLOR_TYPE_GRAY_ALPHA
+ (bit depths 8, 16)
+ PNG_COLOR_TYPE_PALETTE
+ (bit depths 1, 2, 4, 8)
+ PNG_COLOR_TYPE_RGB
+ (bit_depths 8, 16)
+ PNG_COLOR_TYPE_RGB_ALPHA
+ (bit_depths 8, 16)
+
+ PNG_COLOR_MASK_PALETTE
+ PNG_COLOR_MASK_COLOR
+ PNG_COLOR_MASK_ALPHA
+
+ filter_method - (must be PNG_FILTER_TYPE_BASE
+ for PNG 1.0, and can also be
+ PNG_INTRAPIXEL_DIFFERENCING if
+ the PNG datastream is embedded in
+ a MNG-1.0 datastream)
+ compression_type - (must be PNG_COMPRESSION_TYPE_BASE
+ for PNG 1.0)
+ interlace_type - (PNG_INTERLACE_NONE or
+ PNG_INTERLACE_ADAM7)
+ Any or all of interlace_type, compression_type, of
+ filter_method can be NULL if you are
+ not interested in their values.
+
+ channels = png_get_channels(png_ptr, info_ptr);
+ channels - number of channels of info for the
+ color type (valid values are 1 (GRAY,
+ PALETTE), 2 (GRAY_ALPHA), 3 (RGB),
+ 4 (RGB_ALPHA or RGB + filler byte))
+ rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+ rowbytes - number of bytes needed to hold a row
+
+ signature = png_get_signature(png_ptr, info_ptr);
+ signature - holds the signature read from the
+ file (if any). The data is kept in
+ the same offset it would be if the
+ whole signature were read (i.e. if an
+ application had already read in 4
+ bytes of signature before starting
+ libpng, the remaining 4 bytes would
+ be in signature[4] through signature[7]
+ (see png_set_sig_bytes())).
+
+
+ width = png_get_image_width(png_ptr,
+ info_ptr);
+ height = png_get_image_height(png_ptr,
+ info_ptr);
+ bit_depth = png_get_bit_depth(png_ptr,
+ info_ptr);
+ color_type = png_get_color_type(png_ptr,
+ info_ptr);
+ filter_method = png_get_filter_type(png_ptr,
+ info_ptr);
+ compression_type = png_get_compression_type(png_ptr,
+ info_ptr);
+ interlace_type = png_get_interlace_type(png_ptr,
+ info_ptr);
+
+
+These are also important, but their validity depends on whether the chunk
+has been read. The png_get_valid(png_ptr, info_ptr, PNG_INFO_<chunk>) and
+png_get_<chunk>(png_ptr, info_ptr, ...) functions return non-zero if the
+data has been read, or zero if it is missing. The parameters to the
+png_get_<chunk> are set directly if they are simple data types, or a pointer
+into the info_ptr is returned for any complex types.
+
+ png_get_PLTE(png_ptr, info_ptr, &palette,
+ &num_palette);
+ palette - the palette for the file
+ (array of png_color)
+ num_palette - number of entries in the palette
+
+ png_get_gAMA(png_ptr, info_ptr, &gamma);
+ gamma - the gamma the file is written
+ at (PNG_INFO_gAMA)
+
+ png_get_sRGB(png_ptr, info_ptr, &srgb_intent);
+ srgb_intent - the rendering intent (PNG_INFO_sRGB)
+ The presence of the sRGB chunk
+ means that the pixel data is in the
+ sRGB color space. This chunk also
+ implies specific values of gAMA and
+ cHRM.
+
+ png_get_iCCP(png_ptr, info_ptr, &name,
+ &compression_type, &profile, &proflen);
+ name - The profile name.
+ compression - The compression type; always
+ PNG_COMPRESSION_TYPE_BASE for PNG 1.0.
+ You may give NULL to this argument to
+ ignore it.
+ profile - International Color Consortium color
+ profile data. May contain NULs.
+ proflen - length of profile data in bytes.
+
+ png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+ sig_bit - the number of significant bits for
+ (PNG_INFO_sBIT) each of the gray,
+ red, green, and blue channels,
+ whichever are appropriate for the
+ given color type (png_color_16)
+
+ png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
+ &trans_values);
+ trans - array of transparent entries for
+ palette (PNG_INFO_tRNS)
+ trans_values - graylevel or color sample values of
+ the single transparent color for
+ non-paletted images (PNG_INFO_tRNS)
+ num_trans - number of transparent entries
+ (PNG_INFO_tRNS)
+
+ png_get_hIST(png_ptr, info_ptr, &hist);
+ (PNG_INFO_hIST)
+ hist - histogram of palette (array of
+ png_uint_16)
+
+ png_get_tIME(png_ptr, info_ptr, &mod_time);
+ mod_time - time image was last modified
+ (PNG_VALID_tIME)
+
+ png_get_bKGD(png_ptr, info_ptr, &background);
+ background - background color (PNG_VALID_bKGD)
+ valid 16-bit red, green and blue
+ values, regardless of color_type
+
+ num_comments = png_get_text(png_ptr, info_ptr,
+ &text_ptr, &num_text);
+ num_comments - number of comments
+ text_ptr - array of png_text holding image
+ comments
+ text_ptr[i].compression - type of compression used
+ on "text" PNG_TEXT_COMPRESSION_NONE
+ PNG_TEXT_COMPRESSION_zTXt
+ PNG_ITXT_COMPRESSION_NONE
+ PNG_ITXT_COMPRESSION_zTXt
+ text_ptr[i].key - keyword for comment. Must contain
+ 1-79 characters.
+ text_ptr[i].text - text comments for current
+ keyword. Can be empty.
+ text_ptr[i].text_length - length of text string,
+ after decompression, 0 for iTXt
+ text_ptr[i].itxt_length - length of itxt string,
+ after decompression, 0 for tEXt/zTXt
+ text_ptr[i].lang - language of comment (empty
+ string for unknown).
+ text_ptr[i].lang_key - keyword in UTF-8
+ (empty string for unknown).
+ num_text - number of comments (same as
+ num_comments; you can put NULL here
+ to avoid the duplication)
+ Note while png_set_text() will accept text, language,
+ and translated keywords that can be NULL pointers, the
+ structure returned by png_get_text will always contain
+ regular zero-terminated C strings. They might be
+ empty strings but they will never be NULL pointers.
+
+ num_spalettes = png_get_sPLT(png_ptr, info_ptr,
+ &palette_ptr);
+ palette_ptr - array of palette structures holding
+ contents of one or more sPLT chunks
+ read.
+ num_spalettes - number of sPLT chunks read.
+
+ png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y,
+ &unit_type);
+ offset_x - positive offset from the left edge
+ of the screen
+ offset_y - positive offset from the top edge
+ of the screen
+ unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
+
+ png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y,
+ &unit_type);
+ res_x - pixels/unit physical resolution in
+ x direction
+ res_y - pixels/unit physical resolution in
+ x direction
+ unit_type - PNG_RESOLUTION_UNKNOWN,
+ PNG_RESOLUTION_METER
+
+ png_get_sCAL(png_ptr, info_ptr, &unit, &width,
+ &height)
+ unit - physical scale units (an integer)
+ width - width of a pixel in physical scale units
+ height - height of a pixel in physical scale units
+ (width and height are doubles)
+
+ png_get_sCAL_s(png_ptr, info_ptr, &unit, &width,
+ &height)
+ unit - physical scale units (an integer)
+ width - width of a pixel in physical scale units
+ height - height of a pixel in physical scale units
+ (width and height are strings like "2.54")
+
+ num_unknown_chunks = png_get_unknown_chunks(png_ptr,
+ info_ptr, &unknowns)
+ unknowns - array of png_unknown_chunk
+ structures holding unknown chunks
+ unknowns[i].name - name of unknown chunk
+ unknowns[i].data - data of unknown chunk
+ unknowns[i].size - size of unknown chunk's data
+ unknowns[i].location - position of chunk in file
+
+ The value of "i" corresponds to the order in which the
+ chunks were read from the PNG file or inserted with the
+ png_set_unknown_chunks() function.
+
+The data from the pHYs chunk can be retrieved in several convenient
+forms:
+
+ res_x = png_get_x_pixels_per_meter(png_ptr,
+ info_ptr)
+ res_y = png_get_y_pixels_per_meter(png_ptr,
+ info_ptr)
+ res_x_and_y = png_get_pixels_per_meter(png_ptr,
+ info_ptr)
+ res_x = png_get_x_pixels_per_inch(png_ptr,
+ info_ptr)
+ res_y = png_get_y_pixels_per_inch(png_ptr,
+ info_ptr)
+ res_x_and_y = png_get_pixels_per_inch(png_ptr,
+ info_ptr)
+ aspect_ratio = png_get_pixel_aspect_ratio(png_ptr,
+ info_ptr)
+
+ (Each of these returns 0 [signifying "unknown"] if
+ the data is not present or if res_x is 0;
+ res_x_and_y is 0 if res_x != res_y)
+
+The data from the oFFs chunk can be retrieved in several convenient
+forms:
+
+ x_offset = png_get_x_offset_microns(png_ptr, info_ptr);
+ y_offset = png_get_y_offset_microns(png_ptr, info_ptr);
+ x_offset = png_get_x_offset_inches(png_ptr, info_ptr);
+ y_offset = png_get_y_offset_inches(png_ptr, info_ptr);
+
+ (Each of these returns 0 [signifying "unknown" if both
+ x and y are 0] if the data is not present or if the
+ chunk is present but the unit is the pixel)
+
+For more information, see the png_info definition in png.h and the
+PNG specification for chunk contents. Be careful with trusting
+rowbytes, as some of the transformations could increase the space
+needed to hold a row (expand, filler, gray_to_rgb, etc.).
+See png_read_update_info(), below.
+
+A quick word about text_ptr and num_text. PNG stores comments in
+keyword/text pairs, one pair per chunk, with no limit on the number
+of text chunks, and a 2^31 byte limit on their size. While there are
+suggested keywords, there is no requirement to restrict the use to these
+strings. It is strongly suggested that keywords and text be sensible
+to humans (that's the point), so don't use abbreviations. Non-printing
+symbols are not allowed. See the PNG specification for more details.
+There is also no requirement to have text after the keyword.
+
+Keywords should be limited to 79 Latin-1 characters without leading or
+trailing spaces, but non-consecutive spaces are allowed within the
+keyword. It is possible to have the same keyword any number of times.
+The text_ptr is an array of png_text structures, each holding a
+pointer to a language string, a pointer to a keyword and a pointer to
+a text string. The text string, language code, and translated
+keyword may be empty or NULL pointers. The keyword/text
+pairs are put into the array in the order that they are received.
+However, some or all of the text chunks may be after the image, so, to
+make sure you have read all the text chunks, don't mess with these
+until after you read the stuff after the image. This will be
+mentioned again below in the discussion that goes with png_read_end().
+
+Input transformations
+
+After you've read the header information, you can set up the library
+to handle any special transformations of the image data. The various
+ways to transform the data will be described in the order that they
+should occur. This is important, as some of these change the color
+type and/or bit depth of the data, and some others only work on
+certain color types and bit depths. Even though each transformation
+checks to see if it has data that it can do something with, you should
+make sure to only enable a transformation if it will be valid for the
+data. For example, don't swap red and blue on grayscale data.
+
+The colors used for the background and transparency values should be
+supplied in the same format/depth as the current image data. They
+are stored in the same format/depth as the image data in a bKGD or tRNS
+chunk, so this is what libpng expects for this data. The colors are
+transformed to keep in sync with the image data when an application
+calls the png_read_update_info() routine (see below).
+
+Data will be decoded into the supplied row buffers packed into bytes
+unless the library has been told to transform it into another format.
+For example, 4 bit/pixel paletted or grayscale data will be returned
+2 pixels/byte with the leftmost pixel in the high-order bits of the
+byte, unless png_set_packing() is called. 8-bit RGB data will be stored
+in RGB RGB RGB format unless png_set_filler() or png_set_add_alpha()
+is called to insert filler bytes, either before or after each RGB triplet.
+16-bit RGB data will be returned RRGGBB RRGGBB, with the most significant
+byte of the color value first, unless png_set_strip_16() is called to
+transform it to regular RGB RGB triplets, or png_set_filler() or
+png_set_add alpha() is called to insert filler bytes, either before or
+after each RRGGBB triplet. Similarly, 8-bit or 16-bit grayscale data can
+be modified with
+png_set_filler(), png_set_add_alpha(), or png_set_strip_16().
+
+The following code transforms grayscale images of less than 8 to 8 bits,
+changes paletted images to RGB, and adds a full alpha channel if there is
+transparency information in a tRNS chunk. This is most useful on
+grayscale images with bit depths of 2 or 4 or if there is a multiple-image
+viewing application that wishes to treat all images in the same way.
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY &&
+ bit_depth < 8) png_set_gray_1_2_4_to_8(png_ptr);
+
+ if (png_get_valid(png_ptr, info_ptr,
+ PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);
+
+These three functions are actually aliases for png_set_expand(), added
+in libpng version 1.0.4, with the function names expanded to improve code
+readability. In some future version they may actually do different
+things.
+
+PNG can have files with 16 bits per channel. If you only can handle
+8 bits per channel, this will strip the pixels down to 8 bit.
+
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+If, for some reason, you don't need the alpha channel on an image,
+and you want to remove it rather than combining it with the background
+(but the image author certainly had in mind that you *would* combine
+it with the background, so that's what you should probably do):
+
+ if (color_type & PNG_COLOR_MASK_ALPHA)
+ png_set_strip_alpha(png_ptr);
+
+In PNG files, the alpha channel in an image
+is the level of opacity. If you need the alpha channel in an image to
+be the level of transparency instead of opacity, you can invert the
+alpha channel (or the tRNS chunk data) after it's read, so that 0 is
+fully opaque and 255 (in 8-bit or paletted images) or 65535 (in 16-bit
+images) is fully transparent, with
+
+ png_set_invert_alpha(png_ptr);
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit
+files. This code expands to 1 pixel per byte without changing the
+values of the pixels:
+
+ if (bit_depth < 8)
+ png_set_packing(png_ptr);
+
+PNG files have possible bit depths of 1, 2, 4, 8, and 16. All pixels
+stored in a PNG image have been "scaled" or "shifted" up to the next
+higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to
+8 bits/sample in the range [0, 255]). However, it is also possible to
+convert the PNG pixel data back to the original bit depth of the image.
+This call reduces the pixels back down to the original bit depth:
+
+ png_color_8p sig_bit;
+
+ if (png_get_sBIT(png_ptr, info_ptr, &sig_bit))
+ png_set_shift(png_ptr, sig_bit);
+
+PNG files store 3-color pixels in red, green, blue order. This code
+changes the storage of the pixels to blue, green, red:
+
+ if (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_bgr(png_ptr);
+
+PNG files store RGB pixels packed into 3 or 6 bytes. This code expands them
+into 4 or 8 bytes for windowing systems that need them in this format:
+
+ if (color_type == PNG_COLOR_TYPE_RGB)
+ png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE);
+
+where "filler" is the 8 or 16-bit number to fill with, and the location is
+either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether
+you want the filler before the RGB or after. This transformation
+does not affect images that already have full alpha channels. To add an
+opaque alpha channel, use filler=0xff or 0xffff and PNG_FILLER_AFTER which
+will generate RGBA pixels.
+
+Note that png_set_filler() does not change the color type. If you want
+to do that, you can add a true alpha channel with
+
+ if (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_GRAY)
+ png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER);
+
+where "filler" contains the alpha value to assign to each pixel.
+This function was added in libpng-1.2.7.
+
+If you are reading an image with an alpha channel, and you need the
+data as ARGB instead of the normal PNG format RGBA:
+
+ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_swap_alpha(png_ptr);
+
+For some uses, you may want a grayscale image to be represented as
+RGB. This code will do that conversion:
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+Conversely, you can convert an RGB or RGBA image to grayscale or grayscale
+with alpha.
+
+ if (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_rgb_to_gray_fixed(png_ptr, error_action,
+ int red_weight, int green_weight);
+
+ error_action = 1: silently do the conversion
+ error_action = 2: issue a warning if the original
+ image has any pixel where
+ red != green or red != blue
+ error_action = 3: issue an error and abort the
+ conversion if the original
+ image has any pixel where
+ red != green or red != blue
+
+ red_weight: weight of red component times 100000
+ green_weight: weight of green component times 100000
+ If either weight is negative, default
+ weights (21268, 71514) are used.
+
+If you have set error_action = 1 or 2, you can
+later check whether the image really was gray, after processing
+the image rows, with the png_get_rgb_to_gray_status(png_ptr) function.
+It will return a png_byte that is zero if the image was gray or
+1 if there were any non-gray pixels. bKGD and sBIT data
+will be silently converted to grayscale, using the green channel
+data, regardless of the error_action setting.
+
+With red_weight+green_weight<=100000,
+the normalized graylevel is computed:
+
+ int rw = red_weight * 65536;
+ int gw = green_weight * 65536;
+ int bw = 65536 - (rw + gw);
+ gray = (rw*red + gw*green + bw*blue)/65536;
+
+The default values approximate those recommended in the Charles
+Poynton's Color FAQ, <http://www.inforamp.net/~poynton/>
+Copyright (c) 1998-01-04 Charles Poynton <poynton at inforamp.net>
+
+ Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+
+Libpng approximates this with
+
+ Y = 0.21268 * R + 0.7151 * G + 0.07217 * B
+
+which can be expressed with integers as
+
+ Y = (6969 * R + 23434 * G + 2365 * B)/32768
+
+The calculation is done in a linear colorspace, if the image gamma
+is known.
+
+If you have a grayscale and you are using png_set_expand_depth(),
+png_set_expand(), or png_set_gray_to_rgb to change to truecolor or to
+a higher bit-depth, you must either supply the background color as a gray
+value at the original file bit-depth (need_expand = 1) or else supply the
+background color as an RGB triplet at the final, expanded bit depth
+(need_expand = 0). Similarly, if you are reading a paletted image, you
+must either supply the background color as a palette index (need_expand = 1)
+or as an RGB triplet that may or may not be in the palette (need_expand = 0).
+
+ png_color_16 my_background;
+ png_color_16p image_background;
+
+ if (png_get_bKGD(png_ptr, info_ptr, &image_background))
+ png_set_background(png_ptr, image_background,
+ PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+ else
+ png_set_background(png_ptr, &my_background,
+ PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+
+The png_set_background() function tells libpng to composite images
+with alpha or simple transparency against the supplied background
+color. If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid),
+you may use this color, or supply another color more suitable for
+the current display (e.g., the background color from a web page). You
+need to tell libpng whether the color is in the gamma space of the
+display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file
+(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one
+that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't
+know why anyone would use this, but it's here).
+
+To properly display PNG images on any kind of system, the application needs
+to know what the display gamma is. Ideally, the user will know this, and
+the application will allow them to set it. One method of allowing the user
+to set the display gamma separately for each system is to check for a
+SCREEN_GAMMA or DISPLAY_GAMMA environment variable, which will hopefully be
+correctly set.
+
+Note that display_gamma is the overall gamma correction required to produce
+pleasing results, which depends on the lighting conditions in the surrounding
+environment. In a dim or brightly lit room, no compensation other than
+the physical gamma exponent of the monitor is needed, while in a dark room
+a slightly smaller exponent is better.
+
+ double gamma, screen_gamma;
+
+ if (/* We have a user-defined screen
+ gamma value */)
+ {
+ screen_gamma = user_defined_screen_gamma;
+ }
+ /* One way that applications can share the same
+ screen gamma value */
+ else if ((gamma_str = getenv("SCREEN_GAMMA"))
+ != NULL)
+ {
+ screen_gamma = (double)atof(gamma_str);
+ }
+ /* If we don't have another value */
+ else
+ {
+ screen_gamma = 2.2; /* A good guess for a
+ PC monitor in a bright office or a dim room */
+ screen_gamma = 2.0; /* A good guess for a
+ PC monitor in a dark room */
+ screen_gamma = 1.7 or 1.0; /* A good
+ guess for Mac systems */
+ }
+
+The png_set_gamma() function handles gamma transformations of the data.
+Pass both the file gamma and the current screen_gamma. If the file does
+not have a gamma value, you can pass one anyway if you have an idea what
+it is (usually 0.45455 is a good guess for GIF images on PCs). Note
+that file gammas are inverted from screen gammas. See the discussions
+on gamma in the PNG specification for an excellent description of what
+gamma is, and why all applications should support it. It is strongly
+recommended that PNG viewers support gamma correction.
+
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+ png_set_gamma(png_ptr, screen_gamma, gamma);
+ else
+ png_set_gamma(png_ptr, screen_gamma, 0.45455);
+
+If you need to reduce an RGB file to a paletted file, or if a paletted
+file has more entries then will fit on your screen, png_set_dither()
+will do that. Note that this is a simple match dither that merely
+finds the closest color available. This should work fairly well with
+optimized palettes, and fairly badly with linear color cubes. If you
+pass a palette that is larger then maximum_colors, the file will
+reduce the number of colors in the palette so it will fit into
+maximum_colors. If there is a histogram, it will use it to make
+more intelligent choices when reducing the palette. If there is no
+histogram, it may not do as good a job.
+
+ if (color_type & PNG_COLOR_MASK_COLOR)
+ {
+ if (png_get_valid(png_ptr, info_ptr,
+ PNG_INFO_PLTE))
+ {
+ png_uint_16p histogram = NULL;
+
+ png_get_hIST(png_ptr, info_ptr,
+ &histogram);
+ png_set_dither(png_ptr, palette, num_palette,
+ max_screen_colors, histogram, 1);
+ }
+ else
+ {
+ png_color std_color_cube[MAX_SCREEN_COLORS] =
+ { ... colors ... };
+
+ png_set_dither(png_ptr, std_color_cube,
+ MAX_SCREEN_COLORS, MAX_SCREEN_COLORS,
+ NULL,0);
+ }
+ }
+
+PNG files describe monochrome as black being zero and white being one.
+The following code will reverse this (make black be one and white be
+zero):
+
+ if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY)
+ png_set_invert_mono(png_ptr);
+
+This function can also be used to invert grayscale and gray-alpha images:
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_invert_mono(png_ptr);
+
+PNG files store 16 bit pixels in network byte order (big-endian,
+ie. most significant bits first). This code changes the storage to the
+other way (little-endian, i.e. least significant bits first, the
+way PCs store them):
+
+ if (bit_depth == 16)
+ png_set_swap(png_ptr);
+
+If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you
+need to change the order the pixels are packed into bytes, you can use:
+
+ if (bit_depth < 8)
+ png_set_packswap(png_ptr);
+
+Finally, you can write your own transformation function if none of
+the existing ones meets your needs. This is done by setting a callback
+with
+
+ png_set_read_user_transform_fn(png_ptr,
+ read_transform_fn);
+
+You must supply the function
+
+ void read_transform_fn(png_ptr ptr, row_info_ptr
+ row_info, png_bytep data)
+
+See pngtest.c for a working example. Your function will be called
+after all of the other transformations have been processed.
+
+You can also set up a pointer to a user structure for use by your
+callback function, and you can inform libpng that your transform
+function will change the number of channels or bit depth with the
+function
+
+ png_set_user_transform_info(png_ptr, user_ptr,
+ user_depth, user_channels);
+
+The user's application, not libpng, is responsible for allocating and
+freeing any memory required for the user structure.
+
+You can retrieve the pointer via the function
+png_get_user_transform_ptr(). For example:
+
+ voidp read_user_transform_ptr =
+ png_get_user_transform_ptr(png_ptr);
+
+The last thing to handle is interlacing; this is covered in detail below,
+but you must call the function here if you want libpng to handle expansion
+of the interlaced image.
+
+ number_of_passes = png_set_interlace_handling(png_ptr);
+
+After setting the transformations, libpng can update your png_info
+structure to reflect any transformations you've requested with this
+call. This is most useful to update the info structure's rowbytes
+field so you can use it to allocate your image memory. This function
+will also update your palette with the correct screen_gamma and
+background if these have been given with the calls above.
+
+ png_read_update_info(png_ptr, info_ptr);
+
+After you call png_read_update_info(), you can allocate any
+memory you need to hold the image. The row data is simply
+raw byte data for all forms of images. As the actual allocation
+varies among applications, no example will be given. If you
+are allocating one large chunk, you will need to build an
+array of pointers to each row, as it will be needed for some
+of the functions below.
+
+Reading image data
+
+After you've allocated memory, you can read the image data.
+The simplest way to do this is in one function call. If you are
+allocating enough memory to hold the whole image, you can just
+call png_read_image() and libpng will read in all the image data
+and put it in the memory area supplied. You will need to pass in
+an array of pointers to each row.
+
+This function automatically handles interlacing, so you don't need
+to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_read_rows().
+
+ png_read_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+ png_bytep row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to read in the whole image at once, you can
+use png_read_rows() instead. If there is no interlacing (check
+interlace_type == PNG_INTERLACE_NONE), this is simple:
+
+ png_read_rows(png_ptr, row_pointers, NULL,
+ number_of_rows);
+
+where row_pointers is the same as in the png_read_image() call.
+
+If you are doing this just one row at a time, you can do this with
+a single row_pointer instead of an array of row_pointers:
+
+ png_bytep row_pointer = row;
+ png_read_row(png_ptr, row_pointer, NULL);
+
+If the file is interlaced (interlace_type != 0 in the IHDR chunk), things
+get somewhat harder. The only current (PNG Specification version 1.2)
+interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7)
+is a somewhat complicated 2D interlace scheme, known as Adam7, that
+breaks down an image into seven smaller images of varying size, based
+on an 8x8 grid.
+
+libpng can fill out those images or it can give them to you "as is".
+If you want them filled out, there are two ways to do that. The one
+mentioned in the PNG specification is to expand each pixel to cover
+those pixels that have not been read yet (the "rectangle" method).
+This results in a blocky image for the first pass, which gradually
+smooths out as more pixels are read. The other method is the "sparkle"
+method, where pixels are drawn only in their final locations, with the
+rest of the image remaining whatever colors they were initialized to
+before the start of the read. The first method usually looks better,
+but tends to be slower, as there are more pixels to put in the rows.
+
+If you don't want libpng to handle the interlacing details, just call
+png_read_rows() seven times to read in all seven images. Each of the
+images is a valid image by itself, or they can all be combined on an
+8x8 grid to form a single image (although if you intend to combine them
+you would be far better off using the libpng interlace handling).
+
+The first pass will return an image 1/8 as wide as the entire image
+(every 8th column starting in column 0) and 1/8 as high as the original
+(every 8th row starting in row 0), the second will be 1/8 as wide
+(starting in column 4) and 1/8 as high (also starting in row 0). The
+third pass will be 1/4 as wide (every 4th pixel starting in column 0) and
+1/8 as high (every 8th row starting in row 4), and the fourth pass will
+be 1/4 as wide and 1/4 as high (every 4th column starting in column 2,
+and every 4th row starting in row 0). The fifth pass will return an
+image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2),
+while the sixth pass will be 1/2 as wide and 1/2 as high as the original
+(starting in column 1 and row 0). The seventh and final pass will be as
+wide as the original, and 1/2 as high, containing all of the odd
+numbered scanlines. Phew!
+
+If you want libpng to expand the images, call this before calling
+png_start_read_image() or png_read_update_info():
+
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ number_of_passes
+ = png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed. Currently, this
+is seven, but may change if another interlace type is added.
+This function can be called even if the file is not interlaced,
+where it will return one pass.
+
+If you are not going to display the image after each pass, but are
+going to wait until the entire image is read in, use the sparkle
+effect. This effect is faster and the end result of either method
+is exactly the same. If you are planning on displaying the image
+after each pass, the "rectangle" effect is generally considered the
+better looking one.
+
+If you only want the "sparkle" effect, just call png_read_rows() as
+normal, with the third parameter NULL. Make sure you make pass over
+the image number_of_passes times, and you don't change the data in the
+rows between calls. You can change the locations of the data, just
+not the data. Each pass only writes the pixels appropriate for that
+pass, and assumes the data from previous passes is still valid.
+
+ png_read_rows(png_ptr, row_pointers, NULL,
+ number_of_rows);
+
+If you only want the first effect (the rectangles), do the same as
+before except pass the row buffer in the third parameter, and leave
+the second parameter NULL.
+
+ png_read_rows(png_ptr, NULL, row_pointers,
+ number_of_rows);
+
+Finishing a sequential read
+
+After you are finished reading the image through either the high- or
+low-level interfaces, you can finish reading the file. If you are
+interested in comments or time, which may be stored either before or
+after the image data, you should pass the separate png_info struct if
+you want to keep the comments from before and after the image
+separate. If you are not interested, you can pass NULL.
+
+ png_read_end(png_ptr, end_info);
+
+When you are done, you can free all memory allocated by libpng like this:
+
+ png_destroy_read_struct(&png_ptr, &info_ptr,
+ &end_info);
+
+It is also possible to individually free the info_ptr members that
+point to libpng-allocated storage with the following function:
+
+ png_free_data(png_ptr, info_ptr, mask, seq)
+ mask - identifies data to be freed, a mask
+ containing the logical OR of one or
+ more of
+ PNG_FREE_PLTE, PNG_FREE_TRNS,
+ PNG_FREE_HIST, PNG_FREE_ICCP,
+ PNG_FREE_PCAL, PNG_FREE_ROWS,
+ PNG_FREE_SCAL, PNG_FREE_SPLT,
+ PNG_FREE_TEXT, PNG_FREE_UNKN,
+ or simply PNG_FREE_ALL
+ seq - sequence number of item to be freed
+ (-1 for all items)
+
+This function may be safely called when the relevant storage has
+already been freed, or has not yet been allocated, or was allocated
+by the user and not by libpng, and will in those
+cases do nothing. The "seq" parameter is ignored if only one item
+of the selected data type, such as PLTE, is allowed. If "seq" is not
+-1, and multiple items are allowed for the data type identified in
+the mask, such as text or sPLT, only the n'th item in the structure
+is freed, where n is "seq".
+
+The default behavior is only to free data that was allocated internally
+by libpng. This can be changed, so that libpng will not free the data,
+or so that it will free data that was allocated by the user with png_malloc()
+or png_zalloc() and passed in via a png_set_*() function, with
+
+ png_data_freer(png_ptr, info_ptr, freer, mask)
+ mask - which data elements are affected
+ same choices as in png_free_data()
+ freer - one of
+ PNG_DESTROY_WILL_FREE_DATA
+ PNG_SET_WILL_FREE_DATA
+ PNG_USER_WILL_FREE_DATA
+
+This function only affects data that has already been allocated.
+You can call this function after reading the PNG data but before calling
+any png_set_*() functions, to control whether the user or the png_set_*()
+function is responsible for freeing any existing data that might be present,
+and again after the png_set_*() functions to control whether the user
+or png_destroy_*() is supposed to free the data. When the user assumes
+responsibility for libpng-allocated data, the application must use
+png_free() to free it, and when the user transfers responsibility to libpng
+for data that the user has allocated, the user must have used png_malloc()
+or png_zalloc() to allocate it.
+
+If you allocated your row_pointers in a single block, as suggested above in
+the description of the high level read interface, you must not transfer
+responsibility for freeing it to the png_set_rows or png_read_destroy function,
+because they would also try to free the individual row_pointers[i].
+
+If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword
+separately, do not transfer responsibility for freeing text_ptr to libpng,
+because when libpng fills a png_text structure it combines these members with
+the key member, and png_free_data() will free only text_ptr.key. Similarly,
+if you transfer responsibility for free'ing text_ptr from libpng to your
+application, your application must not separately free those members.
+
+The png_free_data() function will turn off the "valid" flag for anything
+it frees. If you need to turn the flag off for a chunk that was freed by your
+application instead of by libpng, you can use
+
+ png_set_invalid(png_ptr, info_ptr, mask);
+ mask - identifies the chunks to be made invalid,
+ containing the logical OR of one or
+ more of
+ PNG_INFO_gAMA, PNG_INFO_sBIT,
+ PNG_INFO_cHRM, PNG_INFO_PLTE,
+ PNG_INFO_tRNS, PNG_INFO_bKGD,
+ PNG_INFO_hIST, PNG_INFO_pHYs,
+ PNG_INFO_oFFs, PNG_INFO_tIME,
+ PNG_INFO_pCAL, PNG_INFO_sRGB,
+ PNG_INFO_iCCP, PNG_INFO_sPLT,
+ PNG_INFO_sCAL, PNG_INFO_IDAT
+
+For a more compact example of reading a PNG image, see the file example.c.
+
+Reading PNG files progressively
+
+The progressive reader is slightly different then the non-progressive
+reader. Instead of calling png_read_info(), png_read_rows(), and
+png_read_end(), you make one call to png_process_data(), which calls
+callbacks when it has the info, a row, or the end of the image. You
+set up these callbacks with png_set_progressive_read_fn(). You don't
+have to worry about the input/output functions of libpng, as you are
+giving the library the data directly in png_process_data(). I will
+assume that you have read the section on reading PNG files above,
+so I will only highlight the differences (although I will show
+all of the code).
+
+png_structp png_ptr;
+png_infop info_ptr;
+
+ /* An example code fragment of how you would
+ initialize the progressive reader in your
+ application. */
+ int
+ initialize_png_reader()
+ {
+ png_ptr = png_create_read_struct
+ (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+ user_error_fn, user_warning_fn);
+ if (!png_ptr)
+ return (ERROR);
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct(&png_ptr, (png_infopp)NULL,
+ (png_infopp)NULL);
+ return (ERROR);
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr,
+ (png_infopp)NULL);
+ return (ERROR);
+ }
+
+ /* This one's new. You can provide functions
+ to be called when the header info is valid,
+ when each row is completed, and when the image
+ is finished. If you aren't using all functions,
+ you can specify NULL parameters. Even when all
+ three functions are NULL, you need to call
+ png_set_progressive_read_fn(). You can use
+ any struct as the user_ptr (cast to a void pointer
+ for the function call), and retrieve the pointer
+ from inside the callbacks using the function
+
+ png_get_progressive_ptr(png_ptr);
+
+ which will return a void pointer, which you have
+ to cast appropriately.
+ */
+ png_set_progressive_read_fn(png_ptr, (void *)user_ptr,
+ info_callback, row_callback, end_callback);
+
+ return 0;
+ }
+
+ /* A code fragment that you call as you receive blocks
+ of data */
+ int
+ process_data(png_bytep buffer, png_uint_32 length)
+ {
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr,
+ (png_infopp)NULL);
+ return (ERROR);
+ }
+
+ /* This one's new also. Simply give it a chunk
+ of data from the file stream (in order, of
+ course). On machines with segmented memory
+ models machines, don't give it any more than
+ 64K. The library seems to run fine with sizes
+ of 4K. Although you can give it much less if
+ necessary (I assume you can give it chunks of
+ 1 byte, I haven't tried less then 256 bytes
+ yet). When this function returns, you may
+ want to display any rows that were generated
+ in the row callback if you don't already do
+ so there.
+ */
+ png_process_data(png_ptr, info_ptr, buffer, length);
+ return 0;
+ }
+
+ /* This function is called (as set by
+ png_set_progressive_read_fn() above) when enough data
+ has been supplied so all of the header has been
+ read.
+ */
+ void
+ info_callback(png_structp png_ptr, png_infop info)
+ {
+ /* Do any setup here, including setting any of
+ the transformations mentioned in the Reading
+ PNG files section. For now, you _must_ call
+ either png_start_read_image() or
+ png_read_update_info() after all the
+ transformations are set (even if you don't set
+ any). You may start getting rows before
+ png_process_data() returns, so this is your
+ last chance to prepare for that.
+ */
+ }
+
+ /* This function is called when each row of image
+ data is complete */
+ void
+ row_callback(png_structp png_ptr, png_bytep new_row,
+ png_uint_32 row_num, int pass)
+ {
+ /* If the image is interlaced, and you turned
+ on the interlace handler, this function will
+ be called for every row in every pass. Some
+ of these rows will not be changed from the
+ previous pass. When the row is not changed,
+ the new_row variable will be NULL. The rows
+ and passes are called in order, so you don't
+ really need the row_num and pass, but I'm
+ supplying them because it may make your life
+ easier.
+
+ For the non-NULL rows of interlaced images,
+ you must call png_progressive_combine_row()
+ passing in the row and the old row. You can
+ call this function for NULL rows (it will just
+ return) and for non-interlaced images (it just
+ does the memcpy for you) if it will make the
+ code easier. Thus, you can just do this for
+ all cases:
+ */
+
+ png_progressive_combine_row(png_ptr, old_row,
+ new_row);
+
+ /* where old_row is what was displayed for
+ previously for the row. Note that the first
+ pass (pass == 0, really) will completely cover
+ the old row, so the rows do not have to be
+ initialized. After the first pass (and only
+ for interlaced images), you will have to pass
+ the current row, and the function will combine
+ the old row and the new row.
+ */
+ }
+
+ void
+ end_callback(png_structp png_ptr, png_infop info)
+ {
+ /* This function is called after the whole image
+ has been read, including any chunks after the
+ image (up to and including the IEND). You
+ will usually have the same info chunk as you
+ had in the header, although some data may have
+ been added to the comments and time fields.
+
+ Most people won't do much here, perhaps setting
+ a flag that marks the image as finished.
+ */
+ }
+
+
+
+IV. Writing
+
+Much of this is very similar to reading. However, everything of
+importance is repeated here, so you won't have to constantly look
+back up in the reading section to understand writing.
+
+Setup
+
+You will want to do the I/O initialization before you get into libpng,
+so if it doesn't work, you don't have anything to undo. If you are not
+using the standard I/O functions, you will need to replace them with
+custom writing functions. See the discussion under Customizing libpng.
+
+ FILE *fp = fopen(file_name, "wb");
+ if (!fp)
+ {
+ return (ERROR);
+ }
+
+Next, png_struct and png_info need to be allocated and initialized.
+As these can be both relatively large, you may not want to store these
+on the stack, unless you have stack space to spare. Of course, you
+will want to check if they return NULL. If you are also reading,
+you won't want to name your read structure and your write structure
+both "png_ptr"; you can call them anything you like, such as
+"read_ptr" and "write_ptr". Look at pngtest.c, for example.
+
+ png_structp png_ptr = png_create_write_struct
+ (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+ user_error_fn, user_warning_fn);
+ if (!png_ptr)
+ return (ERROR);
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_write_struct(&png_ptr,
+ (png_infopp)NULL);
+ return (ERROR);
+ }
+
+If you want to use your own memory allocation routines,
+define PNG_USER_MEM_SUPPORTED and use
+png_create_write_struct_2() instead of png_create_write_struct():
+
+ png_structp png_ptr = png_create_write_struct_2
+ (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr,
+ user_error_fn, user_warning_fn, (png_voidp)
+ user_mem_ptr, user_malloc_fn, user_free_fn);
+
+After you have these structures, you will need to set up the
+error handling. When libpng encounters an error, it expects to
+longjmp() back to your routine. Therefore, you will need to call
+setjmp() and pass the png_jmpbuf(png_ptr). If you
+write the file from different routines, you will need to update
+the png_jmpbuf(png_ptr) every time you enter a new routine that will
+call a png_*() function. See your documentation of setjmp/longjmp
+for your compiler for more information on setjmp/longjmp. See
+the discussion on libpng error handling in the Customizing Libpng
+section below for more information on the libpng error handling.
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(fp);
+ return (ERROR);
+ }
+ ...
+ return;
+
+If you would rather avoid the complexity of setjmp/longjmp issues,
+you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case
+errors will result in a call to PNG_ABORT() which defaults to abort().
+
+Now you need to set up the output code. The default for libpng is to
+use the C function fwrite(). If you use this, you will need to pass a
+valid FILE * in the function png_init_io(). Be sure that the file is
+opened in binary mode. Again, if you wish to handle writing data in
+another way, see the discussion on libpng I/O handling in the Customizing
+Libpng section below.
+
+ png_init_io(png_ptr, fp);
+
+Write callbacks
+
+At this point, you can set up a callback function that will be
+called after each row has been written, which you can use to control
+a progress meter or the like. It's demonstrated in pngtest.c.
+You must supply a function
+
+ void write_row_callback(png_ptr, png_uint_32 row,
+ int pass);
+ {
+ /* put your code here */
+ }
+
+(You can give it another name that you like instead of "write_row_callback")
+
+To inform libpng about your function, use
+
+ png_set_write_status_fn(png_ptr, write_row_callback);
+
+You now have the option of modifying how the compression library will
+run. The following functions are mainly for testing, but may be useful
+in some cases, like if you need to write PNG files extremely fast and
+are willing to give up some compression, or if you want to get the
+maximum possible compression at the expense of slower writing. If you
+have no special needs in this area, let the library do what it wants by
+not calling this function at all, as it has been tuned to deliver a good
+speed/compression ratio. The second parameter to png_set_filter() is
+the filter method, for which the only valid values are 0 (as of the
+July 1999 PNG specification, version 1.2) or 64 (if you are writing
+a PNG datastream that is to be embedded in a MNG datastream). The third
+parameter is a flag that indicates which filter type(s) are to be tested
+for each scanline. See the PNG specification for details on the specific filter
+types.
+
+
+ /* turn on or off filtering, and/or choose
+ specific filters. You can use either a single
+ PNG_FILTER_VALUE_NAME or the logical OR of one
+ or more PNG_FILTER_NAME masks. */
+ png_set_filter(png_ptr, 0,
+ PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE |
+ PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB |
+ PNG_FILTER_UP | PNG_FILTER_VALUE_UP |
+ PNG_FILTER_AVE | PNG_FILTER_VALUE_AVE |
+ PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH|
+ PNG_ALL_FILTERS);
+
+If an application
+wants to start and stop using particular filters during compression,
+it should start out with all of the filters (to ensure that the previous
+row of pixels will be stored in case it's needed later), and then add
+and remove them after the start of compression.
+
+If you are writing a PNG datastream that is to be embedded in a MNG
+datastream, the second parameter can be either 0 or 64.
+
+The png_set_compression_*() functions interface to the zlib compression
+library, and should mostly be ignored unless you really know what you are
+doing. The only generally useful call is png_set_compression_level()
+which changes how much time zlib spends on trying to compress the image
+data. See the Compression Library (zlib.h and algorithm.txt, distributed
+with zlib) for details on the compression levels.
+
+ /* set the zlib compression level */
+ png_set_compression_level(png_ptr,
+ Z_BEST_COMPRESSION);
+
+ /* set other zlib parameters */
+ png_set_compression_mem_level(png_ptr, 8);
+ png_set_compression_strategy(png_ptr,
+ Z_DEFAULT_STRATEGY);
+ png_set_compression_window_bits(png_ptr, 15);
+ png_set_compression_method(png_ptr, 8);
+ png_set_compression_buffer_size(png_ptr, 8192)
+
+extern PNG_EXPORT(void,png_set_zbuf_size)
+
+Setting the contents of info for output
+
+You now need to fill in the png_info structure with all the data you
+wish to write before the actual image. Note that the only thing you
+are allowed to write after the image is the text chunks and the time
+chunk (as of PNG Specification 1.2, anyway). See png_write_end() and
+the latest PNG specification for more information on that. If you
+wish to write them before the image, fill them in now, and flag that
+data as being valid. If you want to wait until after the data, don't
+fill them until png_write_end(). For all the fields in png_info and
+their data types, see png.h. For explanations of what the fields
+contain, see the PNG specification.
+
+Some of the more important parts of the png_info are:
+
+ png_set_IHDR(png_ptr, info_ptr, width, height,
+ bit_depth, color_type, interlace_type,
+ compression_type, filter_method)
+ width - holds the width of the image
+ in pixels (up to 2^31).
+ height - holds the height of the image
+ in pixels (up to 2^31).
+ bit_depth - holds the bit depth of one of the
+ image channels.
+ (valid values are 1, 2, 4, 8, 16
+ and depend also on the
+ color_type. See also significant
+ bits (sBIT) below).
+ color_type - describes which color/alpha
+ channels are present.
+ PNG_COLOR_TYPE_GRAY
+ (bit depths 1, 2, 4, 8, 16)
+ PNG_COLOR_TYPE_GRAY_ALPHA
+ (bit depths 8, 16)
+ PNG_COLOR_TYPE_PALETTE
+ (bit depths 1, 2, 4, 8)
+ PNG_COLOR_TYPE_RGB
+ (bit_depths 8, 16)
+ PNG_COLOR_TYPE_RGB_ALPHA
+ (bit_depths 8, 16)
+
+ PNG_COLOR_MASK_PALETTE
+ PNG_COLOR_MASK_COLOR
+ PNG_COLOR_MASK_ALPHA
+
+ interlace_type - PNG_INTERLACE_NONE or
+ PNG_INTERLACE_ADAM7
+ compression_type - (must be
+ PNG_COMPRESSION_TYPE_DEFAULT)
+ filter_method - (must be PNG_FILTER_TYPE_DEFAULT
+ or, if you are writing a PNG to
+ be embedded in a MNG datastream,
+ can also be
+ PNG_INTRAPIXEL_DIFFERENCING)
+
+ png_set_PLTE(png_ptr, info_ptr, palette,
+ num_palette);
+ palette - the palette for the file
+ (array of png_color)
+ num_palette - number of entries in the palette
+
+ png_set_gAMA(png_ptr, info_ptr, gamma);
+ gamma - the gamma the image was created
+ at (PNG_INFO_gAMA)
+
+ png_set_sRGB(png_ptr, info_ptr, srgb_intent);
+ srgb_intent - the rendering intent
+ (PNG_INFO_sRGB) The presence of
+ the sRGB chunk means that the pixel
+ data is in the sRGB color space.
+ This chunk also implies specific
+ values of gAMA and cHRM. Rendering
+ intent is the CSS-1 property that
+ has been defined by the International
+ Color Consortium
+ (http://www.color.org).
+ It can be one of
+ PNG_sRGB_INTENT_SATURATION,
+ PNG_sRGB_INTENT_PERCEPTUAL,
+ PNG_sRGB_INTENT_ABSOLUTE, or
+ PNG_sRGB_INTENT_RELATIVE.
+
+
+ png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr,
+ srgb_intent);
+ srgb_intent - the rendering intent
+ (PNG_INFO_sRGB) The presence of the
+ sRGB chunk means that the pixel
+ data is in the sRGB color space.
+ This function also causes gAMA and
+ cHRM chunks with the specific values
+ that are consistent with sRGB to be
+ written.
+
+ png_set_iCCP(png_ptr, info_ptr, name, compression_type,
+ profile, proflen);
+ name - The profile name.
+ compression - The compression type; always
+ PNG_COMPRESSION_TYPE_BASE for PNG 1.0.
+ You may give NULL to this argument to
+ ignore it.
+ profile - International Color Consortium color
+ profile data. May contain NULs.
+ proflen - length of profile data in bytes.
+
+ png_set_sBIT(png_ptr, info_ptr, sig_bit);
+ sig_bit - the number of significant bits for
+ (PNG_INFO_sBIT) each of the gray, red,
+ green, and blue channels, whichever are
+ appropriate for the given color type
+ (png_color_16)
+
+ png_set_tRNS(png_ptr, info_ptr, trans, num_trans,
+ trans_values);
+ trans - array of transparent entries for
+ palette (PNG_INFO_tRNS)
+ trans_values - graylevel or color sample values of
+ the single transparent color for
+ non-paletted images (PNG_INFO_tRNS)
+ num_trans - number of transparent entries
+ (PNG_INFO_tRNS)
+
+ png_set_hIST(png_ptr, info_ptr, hist);
+ (PNG_INFO_hIST)
+ hist - histogram of palette (array of
+ png_uint_16)
+
+ png_set_tIME(png_ptr, info_ptr, mod_time);
+ mod_time - time image was last modified
+ (PNG_VALID_tIME)
+
+ png_set_bKGD(png_ptr, info_ptr, background);
+ background - background color (PNG_VALID_bKGD)
+
+ png_set_text(png_ptr, info_ptr, text_ptr, num_text);
+ text_ptr - array of png_text holding image
+ comments
+ text_ptr[i].compression - type of compression used
+ on "text" PNG_TEXT_COMPRESSION_NONE
+ PNG_TEXT_COMPRESSION_zTXt
+ PNG_ITXT_COMPRESSION_NONE
+ PNG_ITXT_COMPRESSION_zTXt
+ text_ptr[i].key - keyword for comment. Must contain
+ 1-79 characters.
+ text_ptr[i].text - text comments for current
+ keyword. Can be NULL or empty.
+ text_ptr[i].text_length - length of text string,
+ after decompression, 0 for iTXt
+ text_ptr[i].itxt_length - length of itxt string,
+ after decompression, 0 for tEXt/zTXt
+ text_ptr[i].lang - language of comment (NULL or
+ empty for unknown).
+ text_ptr[i].translated_keyword - keyword in UTF-8 (NULL
+ or empty for unknown).
+ num_text - number of comments
+
+ png_set_sPLT(png_ptr, info_ptr, &palette_ptr,
+ num_spalettes);
+ palette_ptr - array of png_sPLT_struct structures
+ to be added to the list of palettes
+ in the info structure.
+ num_spalettes - number of palette structures to be
+ added.
+
+ png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y,
+ unit_type);
+ offset_x - positive offset from the left
+ edge of the screen
+ offset_y - positive offset from the top
+ edge of the screen
+ unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER
+
+ png_set_pHYs(png_ptr, info_ptr, res_x, res_y,
+ unit_type);
+ res_x - pixels/unit physical resolution
+ in x direction
+ res_y - pixels/unit physical resolution
+ in y direction
+ unit_type - PNG_RESOLUTION_UNKNOWN,
+ PNG_RESOLUTION_METER
+
+ png_set_sCAL(png_ptr, info_ptr, unit, width, height)
+ unit - physical scale units (an integer)
+ width - width of a pixel in physical scale units
+ height - height of a pixel in physical scale units
+ (width and height are doubles)
+
+ png_set_sCAL_s(png_ptr, info_ptr, unit, width, height)
+ unit - physical scale units (an integer)
+ width - width of a pixel in physical scale units
+ height - height of a pixel in physical scale units
+ (width and height are strings like "2.54")
+
+ png_set_unknown_chunks(png_ptr, info_ptr, &unknowns,
+ num_unknowns)
+ unknowns - array of png_unknown_chunk
+ structures holding unknown chunks
+ unknowns[i].name - name of unknown chunk
+ unknowns[i].data - data of unknown chunk
+ unknowns[i].size - size of unknown chunk's data
+ unknowns[i].location - position to write chunk in file
+ 0: do not write chunk
+ PNG_HAVE_IHDR: before PLTE
+ PNG_HAVE_PLTE: before IDAT
+ PNG_AFTER_IDAT: after IDAT
+
+The "location" member is set automatically according to
+what part of the output file has already been written.
+You can change its value after calling png_set_unknown_chunks()
+as demonstrated in pngtest.c. Within each of the "locations",
+the chunks are sequenced according to their position in the
+structure (that is, the value of "i", which is the order in which
+the chunk was either read from the input file or defined with
+png_set_unknown_chunks).
+
+A quick word about text and num_text. text is an array of png_text
+structures. num_text is the number of valid structures in the array.
+Each png_text structure holds a language code, a keyword, a text value,
+and a compression type.
+
+The compression types have the same valid numbers as the compression
+types of the image data. Currently, the only valid number is zero.
+However, you can store text either compressed or uncompressed, unlike
+images, which always have to be compressed. So if you don't want the
+text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE.
+Because tEXt and zTXt chunks don't have a language field, if you
+specify PNG_TEXT_COMPRESSION_NONE or PNG_TEXT_COMPRESSION_zTXt
+any language code or translated keyword will not be written out.
+
+Until text gets around 1000 bytes, it is not worth compressing it.
+After the text has been written out to the file, the compression type
+is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR,
+so that it isn't written out again at the end (in case you are calling
+png_write_end() with the same struct.
+
+The keywords that are given in the PNG Specification are:
+
+ Title Short (one line) title or
+ caption for image
+ Author Name of image's creator
+ Description Description of image (possibly long)
+ Copyright Copyright notice
+ Creation Time Time of original image creation
+ (usually RFC 1123 format, see below)
+ Software Software used to create the image
+ Disclaimer Legal disclaimer
+ Warning Warning of nature of content
+ Source Device used to create the image
+ Comment Miscellaneous comment; conversion
+ from other image format
+
+The keyword-text pairs work like this. Keywords should be short
+simple descriptions of what the comment is about. Some typical
+keywords are found in the PNG specification, as is some recommendations
+on keywords. You can repeat keywords in a file. You can even write
+some text before the image and some after. For example, you may want
+to put a description of the image before the image, but leave the
+disclaimer until after, so viewers working over modem connections
+don't have to wait for the disclaimer to go over the modem before
+they start seeing the image. Finally, keywords should be full
+words, not abbreviations. Keywords and text are in the ISO 8859-1
+(Latin-1) character set (a superset of regular ASCII) and can not
+contain NUL characters, and should not contain control or other
+unprintable characters. To make the comments widely readable, stick
+with basic ASCII, and avoid machine specific character set extensions
+like the IBM-PC character set. The keyword must be present, but
+you can leave off the text string on non-compressed pairs.
+Compressed pairs must have a text string, as only the text string
+is compressed anyway, so the compression would be meaningless.
+
+PNG supports modification time via the png_time structure. Two
+conversion routines are provided, png_convert_from_time_t() for
+time_t and png_convert_from_struct_tm() for struct tm. The
+time_t routine uses gmtime(). You don't have to use either of
+these, but if you wish to fill in the png_time structure directly,
+you should provide the time in universal time (GMT) if possible
+instead of your local time. Note that the year number is the full
+year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and
+that months start with 1.
+
+If you want to store the time of the original image creation, you should
+use a plain tEXt chunk with the "Creation Time" keyword. This is
+necessary because the "creation time" of a PNG image is somewhat vague,
+depending on whether you mean the PNG file, the time the image was
+created in a non-PNG format, a still photo from which the image was
+scanned, or possibly the subject matter itself. In order to facilitate
+machine-readable dates, it is recommended that the "Creation Time"
+tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"),
+although this isn't a requirement. Unlike the tIME chunk, the
+"Creation Time" tEXt chunk is not expected to be automatically changed
+by the software. To facilitate the use of RFC 1123 dates, a function
+png_convert_to_rfc1123(png_timep) is provided to convert from PNG
+time to an RFC 1123 format string.
+
+Writing unknown chunks
+
+You can use the png_set_unknown_chunks function to queue up chunks
+for writing. You give it a chunk name, raw data, and a size; that's
+all there is to it. The chunks will be written by the next following
+png_write_info_before_PLTE, png_write_info, or png_write_end function.
+Any chunks previously read into the info structure's unknown-chunk
+list will also be written out in a sequence that satisfies the PNG
+specification's ordering rules.
+
+The high-level write interface
+
+At this point there are two ways to proceed; through the high-level
+write interface, or through a sequence of low-level write operations.
+You can use the high-level interface if your image data is present
+in the info structure. All defined output
+transformations are permitted, enabled by the following masks.
+
+ PNG_TRANSFORM_IDENTITY No transformation
+ PNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samples
+ PNG_TRANSFORM_PACKSWAP Change order of packed
+ pixels to LSB first
+ PNG_TRANSFORM_INVERT_MONO Invert monochrome images
+ PNG_TRANSFORM_SHIFT Normalize pixels to the
+ sBIT depth
+ PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA
+ to BGRA
+ PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA
+ to AG
+ PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity
+ to transparency
+ PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples
+ PNG_TRANSFORM_STRIP_FILLER Strip out filler bytes.
+
+If you have valid image data in the info structure (you can use
+png_set_rows() to put image data in the info structure), simply do this:
+
+ png_write_png(png_ptr, info_ptr, png_transforms, NULL)
+
+where png_transforms is an integer containing the logical OR of some set of
+transformation flags. This call is equivalent to png_write_info(),
+followed the set of transformations indicated by the transform mask,
+then png_write_image(), and finally png_write_end().
+
+(The final parameter of this call is not yet used. Someday it might point
+to transformation parameters required by some future output transform.)
+
+You must use png_transforms and not call any png_set_transform() functions
+when you use png_write_png().
+
+The low-level write interface
+
+If you are going the low-level route instead, you are now ready to
+write all the file information up to the actual image data. You do
+this with a call to png_write_info().
+
+ png_write_info(png_ptr, info_ptr);
+
+Note that there is one transformation you may need to do before
+png_write_info(). In PNG files, the alpha channel in an image is the
+level of opacity. If your data is supplied as a level of
+transparency, you can invert the alpha channel before you write it, so
+that 0 is fully transparent and 255 (in 8-bit or paletted images) or
+65535 (in 16-bit images) is fully opaque, with
+
+ png_set_invert_alpha(png_ptr);
+
+This must appear before png_write_info() instead of later with the
+other transformations because in the case of paletted images the tRNS
+chunk data has to be inverted before the tRNS chunk is written. If
+your image is not a paletted image, the tRNS data (which in such cases
+represents a single color to be rendered as transparent) won't need to
+be changed, and you can safely do this transformation after your
+png_write_info() call.
+
+If you need to write a private chunk that you want to appear before
+the PLTE chunk when PLTE is present, you can write the PNG info in
+two steps, and insert code to write your own chunk between them:
+
+ png_write_info_before_PLTE(png_ptr, info_ptr);
+ png_set_unknown_chunks(png_ptr, info_ptr, ...);
+ png_write_info(png_ptr, info_ptr);
+
+After you've written the file information, you can set up the library
+to handle any special transformations of the image data. The various
+ways to transform the data will be described in the order that they
+should occur. This is important, as some of these change the color
+type and/or bit depth of the data, and some others only work on
+certain color types and bit depths. Even though each transformation
+checks to see if it has data that it can do something with, you should
+make sure to only enable a transformation if it will be valid for the
+data. For example, don't swap red and blue on grayscale data.
+
+PNG files store RGB pixels packed into 3 or 6 bytes. This code tells
+the library to strip input data that has 4 or 8 bytes per pixel down
+to 3 or 6 bytes (or strip 2 or 4-byte grayscale+filler data to 1 or 2
+bytes per pixel).
+
+ png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+
+where the 0 is unused, and the location is either PNG_FILLER_BEFORE or
+PNG_FILLER_AFTER, depending upon whether the filler byte in the pixel
+is stored XRGB or RGBX.
+
+PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as
+they can, resulting in, for example, 8 pixels per byte for 1 bit files.
+If the data is supplied at 1 pixel per byte, use this code, which will
+correctly pack the pixels into a single byte:
+
+ png_set_packing(png_ptr);
+
+PNG files reduce possible bit depths to 1, 2, 4, 8, and 16. If your
+data is of another bit depth, you can write an sBIT chunk into the
+file so that decoders can recover the original data if desired.
+
+ /* Set the true bit depth of the image data */
+ if (color_type & PNG_COLOR_MASK_COLOR)
+ {
+ sig_bit.red = true_bit_depth;
+ sig_bit.green = true_bit_depth;
+ sig_bit.blue = true_bit_depth;
+ }
+ else
+ {
+ sig_bit.gray = true_bit_depth;
+ }
+ if (color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ sig_bit.alpha = true_bit_depth;
+ }
+
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+If the data is stored in the row buffer in a bit depth other than
+one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG),
+this will scale the values to appear to be the correct bit depth as
+is required by PNG.
+
+ png_set_shift(png_ptr, &sig_bit);
+
+PNG files store 16 bit pixels in network byte order (big-endian,
+ie. most significant bits first). This code would be used if they are
+supplied the other way (little-endian, i.e. least significant bits
+first, the way PCs store them):
+
+ if (bit_depth > 8)
+ png_set_swap(png_ptr);
+
+If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you
+need to change the order the pixels are packed into bytes, you can use:
+
+ if (bit_depth < 8)
+ png_set_packswap(png_ptr);
+
+PNG files store 3 color pixels in red, green, blue order. This code
+would be used if they are supplied as blue, green, red:
+
+ png_set_bgr(png_ptr);
+
+PNG files describe monochrome as black being zero and white being
+one. This code would be used if the pixels are supplied with this reversed
+(black being one and white being zero):
+
+ png_set_invert_mono(png_ptr);
+
+Finally, you can write your own transformation function if none of
+the existing ones meets your needs. This is done by setting a callback
+with
+
+ png_set_write_user_transform_fn(png_ptr,
+ write_transform_fn);
+
+You must supply the function
+
+ void write_transform_fn(png_ptr ptr, row_info_ptr
+ row_info, png_bytep data)
+
+See pngtest.c for a working example. Your function will be called
+before any of the other transformations are processed.
+
+You can also set up a pointer to a user structure for use by your
+callback function.
+
+ png_set_user_transform_info(png_ptr, user_ptr, 0, 0);
+
+The user_channels and user_depth parameters of this function are ignored
+when writing; you can set them to zero as shown.
+
+You can retrieve the pointer via the function png_get_user_transform_ptr().
+For example:
+
+ voidp write_user_transform_ptr =
+ png_get_user_transform_ptr(png_ptr);
+
+It is possible to have libpng flush any pending output, either manually,
+or automatically after a certain number of lines have been written. To
+flush the output stream a single time call:
+
+ png_write_flush(png_ptr);
+
+and to have libpng flush the output stream periodically after a certain
+number of scanlines have been written, call:
+
+ png_set_flush(png_ptr, nrows);
+
+Note that the distance between rows is from the last time png_write_flush()
+was called, or the first row of the image if it has never been called.
+So if you write 50 lines, and then png_set_flush 25, it will flush the
+output on the next scanline, and every 25 lines thereafter, unless
+png_write_flush() is called before 25 more lines have been written.
+If nrows is too small (less than about 10 lines for a 640 pixel wide
+RGB image) the image compression may decrease noticeably (although this
+may be acceptable for real-time applications). Infrequent flushing will
+only degrade the compression performance by a few percent over images
+that do not use flushing.
+
+Writing the image data
+
+That's it for the transformations. Now you can write the image data.
+The simplest way to do this is in one function call. If you have the
+whole image in memory, you can just call png_write_image() and libpng
+will write the image. You will need to pass in an array of pointers to
+each row. This function automatically handles interlacing, so you don't
+need to call png_set_interlace_handling() or call this function multiple
+times, or any of that other stuff necessary with png_write_rows().
+
+ png_write_image(png_ptr, row_pointers);
+
+where row_pointers is:
+
+ png_byte *row_pointers[height];
+
+You can point to void or char or whatever you use for pixels.
+
+If you don't want to write the whole image at once, you can
+use png_write_rows() instead. If the file is not interlaced,
+this is simple:
+
+ png_write_rows(png_ptr, row_pointers,
+ number_of_rows);
+
+row_pointers is the same as in the png_write_image() call.
+
+If you are just writing one row at a time, you can do this with
+a single row_pointer instead of an array of row_pointers:
+
+ png_bytep row_pointer = row;
+
+ png_write_row(png_ptr, row_pointer);
+
+When the file is interlaced, things can get a good deal more
+complicated. The only currently (as of the PNG Specification
+version 1.2, dated July 1999) defined interlacing scheme for PNG files
+is the "Adam7" interlace scheme, that breaks down an
+image into seven smaller images of varying size. libpng will build
+these images for you, or you can do them yourself. If you want to
+build them yourself, see the PNG specification for details of which
+pixels to write when.
+
+If you don't want libpng to handle the interlacing details, just
+use png_set_interlace_handling() and call png_write_rows() the
+correct number of times to write all seven sub-images.
+
+If you want libpng to build the sub-images, call this before you start
+writing any rows:
+
+ number_of_passes =
+ png_set_interlace_handling(png_ptr);
+
+This will return the number of passes needed. Currently, this
+is seven, but may change if another interlace type is added.
+
+Then write the complete image number_of_passes times.
+
+ png_write_rows(png_ptr, row_pointers,
+ number_of_rows);
+
+As some of these rows are not used, and thus return immediately,
+you may want to read about interlacing in the PNG specification,
+and only update the rows that are actually used.
+
+Finishing a sequential write
+
+After you are finished writing the image, you should finish writing
+the file. If you are interested in writing comments or time, you should
+pass an appropriately filled png_info pointer. If you are not interested,
+you can pass NULL.
+
+ png_write_end(png_ptr, info_ptr);
+
+When you are done, you can free all memory used by libpng like this:
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+It is also possible to individually free the info_ptr members that
+point to libpng-allocated storage with the following function:
+
+ png_free_data(png_ptr, info_ptr, mask, seq)
+ mask - identifies data to be freed, a mask
+ containing the logical OR of one or
+ more of
+ PNG_FREE_PLTE, PNG_FREE_TRNS,
+ PNG_FREE_HIST, PNG_FREE_ICCP,
+ PNG_FREE_PCAL, PNG_FREE_ROWS,
+ PNG_FREE_SCAL, PNG_FREE_SPLT,
+ PNG_FREE_TEXT, PNG_FREE_UNKN,
+ or simply PNG_FREE_ALL
+ seq - sequence number of item to be freed
+ (-1 for all items)
+
+This function may be safely called when the relevant storage has
+already been freed, or has not yet been allocated, or was allocated
+by the user and not by libpng, and will in those
+cases do nothing. The "seq" parameter is ignored if only one item
+of the selected data type, such as PLTE, is allowed. If "seq" is not
+-1, and multiple items are allowed for the data type identified in
+the mask, such as text or sPLT, only the n'th item in the structure
+is freed, where n is "seq".
+
+If you allocated data such as a palette that you passed
+in to libpng with png_set_*, you must not free it until just before the call to
+png_destroy_write_struct().
+
+The default behavior is only to free data that was allocated internally
+by libpng. This can be changed, so that libpng will not free the data,
+or so that it will free data that was allocated by the user with png_malloc()
+or png_zalloc() and passed in via a png_set_*() function, with
+
+ png_data_freer(png_ptr, info_ptr, freer, mask)
+ mask - which data elements are affected
+ same choices as in png_free_data()
+ freer - one of
+ PNG_DESTROY_WILL_FREE_DATA
+ PNG_SET_WILL_FREE_DATA
+ PNG_USER_WILL_FREE_DATA
+
+For example, to transfer responsibility for some data from a read structure
+to a write structure, you could use
+
+ png_data_freer(read_ptr, read_info_ptr,
+ PNG_USER_WILL_FREE_DATA,
+ PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST)
+ png_data_freer(write_ptr, write_info_ptr,
+ PNG_DESTROY_WILL_FREE_DATA,
+ PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST)
+
+thereby briefly reassigning responsibility for freeing to the user but
+immediately afterwards reassigning it once more to the write_destroy
+function. Having done this, it would then be safe to destroy the read
+structure and continue to use the PLTE, tRNS, and hIST data in the write
+structure.
+
+This function only affects data that has already been allocated.
+You can call this function before calling after the png_set_*() functions
+to control whether the user or png_destroy_*() is supposed to free the data.
+When the user assumes responsibility for libpng-allocated data, the
+application must use
+png_free() to free it, and when the user transfers responsibility to libpng
+for data that the user has allocated, the user must have used png_malloc()
+or png_zalloc() to allocate it.
+
+If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword
+separately, do not transfer responsibility for freeing text_ptr to libpng,
+because when libpng fills a png_text structure it combines these members with
+the key member, and png_free_data() will free only text_ptr.key. Similarly,
+if you transfer responsibility for free'ing text_ptr from libpng to your
+application, your application must not separately free those members.
+For a more compact example of writing a PNG image, see the file example.c.
+
+V. Modifying/Customizing libpng:
+
+There are three issues here. The first is changing how libpng does
+standard things like memory allocation, input/output, and error handling.
+The second deals with more complicated things like adding new chunks,
+adding new transformations, and generally changing how libpng works.
+Both of those are compile-time issues; that is, they are generally
+determined at the time the code is written, and there is rarely a need
+to provide the user with a means of changing them. The third is a
+run-time issue: choosing between and/or tuning one or more alternate
+versions of computationally intensive routines; specifically, optimized
+assembly-language (and therefore compiler- and platform-dependent)
+versions.
+
+Memory allocation, input/output, and error handling
+
+All of the memory allocation, input/output, and error handling in libpng
+goes through callbacks that are user-settable. The default routines are
+in pngmem.c, pngrio.c, pngwio.c, and pngerror.c, respectively. To change
+these functions, call the appropriate png_set_*_fn() function.
+
+Memory allocation is done through the functions png_malloc()
+and png_free(). These currently just call the standard C functions. If
+your pointers can't access more then 64K at a time, you will want to set
+MAXSEG_64K in zlib.h. Since it is unlikely that the method of handling
+memory allocation on a platform will change between applications, these
+functions must be modified in the library at compile time. If you prefer
+to use a different method of allocating and freeing data, you can use
+png_create_read_struct_2() or png_create_write_struct_2() to register
+your own functions as described above.
+These functions also provide a void pointer that can be retrieved via
+
+ mem_ptr=png_get_mem_ptr(png_ptr);
+
+Your replacement memory functions must have prototypes as follows:
+
+ png_voidp malloc_fn(png_structp png_ptr,
+ png_size_t size);
+ void free_fn(png_structp png_ptr, png_voidp ptr);
+
+Your malloc_fn() must return NULL in case of failure. The png_malloc()
+function will normally call png_error() if it receives a NULL from the
+system memory allocator or from your replacement malloc_fn().
+
+Input/Output in libpng is done through png_read() and png_write(),
+which currently just call fread() and fwrite(). The FILE * is stored in
+png_struct and is initialized via png_init_io(). If you wish to change
+the method of I/O, the library supplies callbacks that you can set
+through the function png_set_read_fn() and png_set_write_fn() at run
+time, instead of calling the png_init_io() function. These functions
+also provide a void pointer that can be retrieved via the function
+png_get_io_ptr(). For example:
+
+ png_set_read_fn(png_structp read_ptr,
+ voidp read_io_ptr, png_rw_ptr read_data_fn)
+
+ png_set_write_fn(png_structp write_ptr,
+ voidp write_io_ptr, png_rw_ptr write_data_fn,
+ png_flush_ptr output_flush_fn);
+
+ voidp read_io_ptr = png_get_io_ptr(read_ptr);
+ voidp write_io_ptr = png_get_io_ptr(write_ptr);
+
+The replacement I/O functions must have prototypes as follows:
+
+ void user_read_data(png_structp png_ptr,
+ png_bytep data, png_size_t length);
+ void user_write_data(png_structp png_ptr,
+ png_bytep data, png_size_t length);
+ void user_flush_data(png_structp png_ptr);
+
+Supplying NULL for the read, write, or flush functions sets them back
+to using the default C stream functions. It is an error to read from
+a write stream, and vice versa.
+
+Error handling in libpng is done through png_error() and png_warning().
+Errors handled through png_error() are fatal, meaning that png_error()
+should never return to its caller. Currently, this is handled via
+setjmp() and longjmp() (unless you have compiled libpng with
+PNG_SETJMP_NOT_SUPPORTED, in which case it is handled via PNG_ABORT()),
+but you could change this to do things like exit() if you should wish.
+
+On non-fatal errors, png_warning() is called
+to print a warning message, and then control returns to the calling code.
+By default png_error() and png_warning() print a message on stderr via
+fprintf() unless the library is compiled with PNG_NO_CONSOLE_IO defined
+(because you don't want the messages) or PNG_NO_STDIO defined (because
+fprintf() isn't available). If you wish to change the behavior of the error
+functions, you will need to set up your own message callbacks. These
+functions are normally supplied at the time that the png_struct is created.
+It is also possible to redirect errors and warnings to your own replacement
+functions after png_create_*_struct() has been called by calling:
+
+ png_set_error_fn(png_structp png_ptr,
+ png_voidp error_ptr, png_error_ptr error_fn,
+ png_error_ptr warning_fn);
+
+ png_voidp error_ptr = png_get_error_ptr(png_ptr);
+
+If NULL is supplied for either error_fn or warning_fn, then the libpng
+default function will be used, calling fprintf() and/or longjmp() if a
+problem is encountered. The replacement error functions should have
+parameters as follows:
+
+ void user_error_fn(png_structp png_ptr,
+ png_const_charp error_msg);
+ void user_warning_fn(png_structp png_ptr,
+ png_const_charp warning_msg);
+
+The motivation behind using setjmp() and longjmp() is the C++ throw and
+catch exception handling methods. This makes the code much easier to write,
+as there is no need to check every return code of every function call.
+However, there are some uncertainties about the status of local variables
+after a longjmp, so the user may want to be careful about doing anything after
+setjmp returns non-zero besides returning itself. Consult your compiler
+documentation for more details. For an alternative approach, you may wish
+to use the "cexcept" facility (see http://cexcept.sourceforge.net).
+
+Custom chunks
+
+If you need to read or write custom chunks, you may need to get deeper
+into the libpng code. The library now has mechanisms for storing
+and writing chunks of unknown type; you can even declare callbacks
+for custom chunks. Hoewver, this may not be good enough if the
+library code itself needs to know about interactions between your
+chunk and existing `intrinsic' chunks.
+
+If you need to write a new intrinsic chunk, first read the PNG
+specification. Acquire a first level of
+understanding of how it works. Pay particular attention to the
+sections that describe chunk names, and look at how other chunks were
+designed, so you can do things similarly. Second, check out the
+sections of libpng that read and write chunks. Try to find a chunk
+that is similar to yours and use it as a template. More details can
+be found in the comments inside the code. It is best to handle unknown
+chunks in a generic method, via callback functions, instead of by
+modifying libpng functions.
+
+If you wish to write your own transformation for the data, look through
+the part of the code that does the transformations, and check out some of
+the simpler ones to get an idea of how they work. Try to find a similar
+transformation to the one you want to add and copy off of it. More details
+can be found in the comments inside the code itself.
+
+Configuring for 16 bit platforms
+
+You will want to look into zconf.h to tell zlib (and thus libpng) that
+it cannot allocate more then 64K at a time. Even if you can, the memory
+won't be accessible. So limit zlib and libpng to 64K by defining MAXSEG_64K.
+
+Configuring for DOS
+
+For DOS users who only have access to the lower 640K, you will
+have to limit zlib's memory usage via a png_set_compression_mem_level()
+call. See zlib.h or zconf.h in the zlib library for more information.
+
+Configuring for Medium Model
+
+Libpng's support for medium model has been tested on most of the popular
+compilers. Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets
+defined, and FAR gets defined to far in pngconf.h, and you should be
+all set. Everything in the library (except for zlib's structure) is
+expecting far data. You must use the typedefs with the p or pp on
+the end for pointers (or at least look at them and be careful). Make
+note that the rows of data are defined as png_bytepp, which is an
+unsigned char far * far *.
+
+Configuring for gui/windowing platforms:
+
+You will need to write new error and warning functions that use the GUI
+interface, as described previously, and set them to be the error and
+warning functions at the time that png_create_*_struct() is called,
+in order to have them available during the structure initialization.
+They can be changed later via png_set_error_fn(). On some compilers,
+you may also have to change the memory allocators (png_malloc, etc.).
+
+Configuring for compiler xxx:
+
+All includes for libpng are in pngconf.h. If you need to add/change/delete
+an include, this is the place to do it. The includes that are not
+needed outside libpng are protected by the PNG_INTERNAL definition,
+which is only defined for those routines inside libpng itself. The
+files in libpng proper only include png.h, which includes pngconf.h.
+
+Configuring zlib:
+
+There are special functions to configure the compression. Perhaps the
+most useful one changes the compression level, which currently uses
+input compression values in the range 0 - 9. The library normally
+uses the default compression level (Z_DEFAULT_COMPRESSION = 6). Tests
+have shown that for a large majority of images, compression values in
+the range 3-6 compress nearly as well as higher levels, and do so much
+faster. For online applications it may be desirable to have maximum speed
+(Z_BEST_SPEED = 1). With versions of zlib after v0.99, you can also
+specify no compression (Z_NO_COMPRESSION = 0), but this would create
+files larger than just storing the raw bitmap. You can specify the
+compression level by calling:
+
+ png_set_compression_level(png_ptr, level);
+
+Another useful one is to reduce the memory level used by the library.
+The memory level defaults to 8, but it can be lowered if you are
+short on memory (running DOS, for example, where you only have 640K).
+Note that the memory level does have an effect on compression; among
+other things, lower levels will result in sections of incompressible
+data being emitted in smaller stored blocks, with a correspondingly
+larger relative overhead of up to 15% in the worst case.
+
+ png_set_compression_mem_level(png_ptr, level);
+
+The other functions are for configuring zlib. They are not recommended
+for normal use and may result in writing an invalid PNG file. See
+zlib.h for more information on what these mean.
+
+ png_set_compression_strategy(png_ptr,
+ strategy);
+ png_set_compression_window_bits(png_ptr,
+ window_bits);
+ png_set_compression_method(png_ptr, method);
+ png_set_compression_buffer_size(png_ptr, size);
+
+Controlling row filtering
+
+If you want to control whether libpng uses filtering or not, which
+filters are used, and how it goes about picking row filters, you
+can call one of these functions. The selection and configuration
+of row filters can have a significant impact on the size and
+encoding speed and a somewhat lesser impact on the decoding speed
+of an image. Filtering is enabled by default for RGB and grayscale
+images (with and without alpha), but not for paletted images nor
+for any images with bit depths less than 8 bits/pixel.
+
+The 'method' parameter sets the main filtering method, which is
+currently only '0' in the PNG 1.2 specification. The 'filters'
+parameter sets which filter(s), if any, should be used for each
+scanline. Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS
+to turn filtering on and off, respectively.
+
+Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB,
+PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise
+ORed together with '|' to specify one or more filters to use.
+These filters are described in more detail in the PNG specification.
+If you intend to change the filter type during the course of writing
+the image, you should start with flags set for all of the filters
+you intend to use so that libpng can initialize its internal
+structures appropriately for all of the filter types. (Note that this
+means the first row must always be adaptively filtered, because libpng
+currently does not allocate the filter buffers until png_write_row()
+is called for the first time.)
+
+ filters = PNG_FILTER_NONE | PNG_FILTER_SUB
+ PNG_FILTER_UP | PNG_FILTER_AVE |
+ PNG_FILTER_PAETH | PNG_ALL_FILTERS;
+
+ png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE,
+ filters);
+ The second parameter can also be
+ PNG_INTRAPIXEL_DIFFERENCING if you are
+ writing a PNG to be embedded in a MNG
+ datastream. This parameter must be the
+ same as the value of filter_method used
+ in png_set_IHDR().
+
+It is also possible to influence how libpng chooses from among the
+available filters. This is done in one or both of two ways - by
+telling it how important it is to keep the same filter for successive
+rows, and by telling it the relative computational costs of the filters.
+
+ double weights[3] = {1.5, 1.3, 1.1},
+ costs[PNG_FILTER_VALUE_LAST] =
+ {1.0, 1.3, 1.3, 1.5, 1.7};
+
+ png_set_filter_heuristics(png_ptr,
+ PNG_FILTER_HEURISTIC_WEIGHTED, 3,
+ weights, costs);
+
+The weights are multiplying factors that indicate to libpng that the
+row filter should be the same for successive rows unless another row filter
+is that many times better than the previous filter. In the above example,
+if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a
+"sum of absolute differences" 1.5 x 1.3 times higher than other filters
+and still be chosen, while the NONE filter could have a sum 1.1 times
+higher than other filters and still be chosen. Unspecified weights are
+taken to be 1.0, and the specified weights should probably be declining
+like those above in order to emphasize recent filters over older filters.
+
+The filter costs specify for each filter type a relative decoding cost
+to be considered when selecting row filters. This means that filters
+with higher costs are less likely to be chosen over filters with lower
+costs, unless their "sum of absolute differences" is that much smaller.
+The costs do not necessarily reflect the exact computational speeds of
+the various filters, since this would unduly influence the final image
+size.
+
+Note that the numbers above were invented purely for this example and
+are given only to help explain the function usage. Little testing has
+been done to find optimum values for either the costs or the weights.
+
+Removing unwanted object code
+
+There are a bunch of #define's in pngconf.h that control what parts of
+libpng are compiled. All the defines end in _SUPPORTED. If you are
+never going to use a capability, you can change the #define to #undef
+before recompiling libpng and save yourself code and data space, or
+you can turn off individual capabilities with defines that begin with
+PNG_NO_.
+
+You can also turn all of the transforms and ancillary chunk capabilities
+off en masse with compiler directives that define
+PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS,
+or all four,
+along with directives to turn on any of the capabilities that you do
+want. The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable
+the extra transformations but still leave the library fully capable of reading
+and writing PNG files with all known public chunks
+Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive
+produces a library that is incapable of reading or writing ancillary chunks.
+If you are not using the progressive reading capability, you can
+turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse
+this with the INTERLACING capability, which you'll still have).
+
+All the reading and writing specific code are in separate files, so the
+linker should only grab the files it needs. However, if you want to
+make sure, or if you are building a stand alone library, all the
+reading files start with pngr and all the writing files start with
+pngw. The files that don't match either (like png.c, pngtrans.c, etc.)
+are used for both reading and writing, and always need to be included.
+The progressive reader is in pngpread.c
+
+If you are creating or distributing a dynamically linked library (a .so
+or DLL file), you should not remove or disable any parts of the library,
+as this will cause applications linked with different versions of the
+library to fail if they call functions not available in your library.
+The size of the library itself should not be an issue, because only
+those sections that are actually used will be loaded into memory.
+
+Requesting debug printout
+
+The macro definition PNG_DEBUG can be used to request debugging
+printout. Set it to an integer value in the range 0 to 3. Higher
+numbers result in increasing amounts of debugging information. The
+information is printed to the "stderr" file, unless another file
+name is specified in the PNG_DEBUG_FILE macro definition.
+
+When PNG_DEBUG > 0, the following functions (macros) become available:
+
+ png_debug(level, message)
+ png_debug1(level, message, p1)
+ png_debug2(level, message, p1, p2)
+
+in which "level" is compared to PNG_DEBUG to decide whether to print
+the message, "message" is the formatted string to be printed,
+and p1 and p2 are parameters that are to be embedded in the string
+according to printf-style formatting directives. For example,
+
+ png_debug1(2, "foo=%d\n", foo);
+
+is expanded to
+
+ if(PNG_DEBUG > 2)
+ fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo);
+
+When PNG_DEBUG is defined but is zero, the macros aren't defined, but you
+can still use PNG_DEBUG to control your own debugging:
+
+ #ifdef PNG_DEBUG
+ fprintf(stderr, ...
+ #endif
+
+When PNG_DEBUG = 1, the macros are defined, but only png_debug statements
+having level = 0 will be printed. There aren't any such statements in
+this version of libpng, but if you insert some they will be printed.
+
+VI. Runtime optimization
+
+A new feature in libpng 1.2.0 is the ability to dynamically switch between
+standard and optimized versions of some routines. Currently these are
+limited to three computationally intensive tasks when reading PNG files:
+decoding row filters, expanding interlacing, and combining interlaced or
+transparent row data with previous row data. Currently the optimized
+versions are available only for x86 (Intel, AMD, etc.) platforms with
+MMX support, though this may change in future versions. (For example,
+the non-MMX assembler optimizations for zlib might become similarly
+runtime-selectable in future releases, in which case libpng could be
+extended to support them. Alternatively, the compile-time choice of
+floating-point versus integer routines for gamma correction might become
+runtime-selectable.)
+
+Because such optimizations tend to be very platform- and compiler-dependent,
+both in how they are written and in how they perform, the new runtime code
+in libpng has been written to allow programs to query, enable, and disable
+either specific optimizations or all such optimizations. For example, to
+enable all possible optimizations (bearing in mind that some "optimizations"
+may actually run more slowly in rare cases):
+
+ #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
+ png_uint_32 mask, flags;
+
+ flags = png_get_asm_flags(png_ptr);
+ mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
+ png_set_asm_flags(png_ptr, flags | mask);
+ #endif
+
+To enable only optimizations relevant to reading PNGs, use PNG_SELECT_READ
+by itself when calling png_get_asm_flagmask(); similarly for optimizing
+only writing. To disable all optimizations:
+
+ #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
+ flags = png_get_asm_flags(png_ptr);
+ mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
+ png_set_asm_flags(png_ptr, flags & ~mask);
+ #endif
+
+To enable or disable only MMX-related features, use png_get_mmx_flagmask()
+in place of png_get_asm_flagmask(). The mmx version takes one additional
+parameter:
+
+ #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
+ int selection = PNG_SELECT_READ | PNG_SELECT_WRITE;
+ int compilerID;
+
+ mask = png_get_mmx_flagmask(selection, &compilerID);
+ #endif
+
+On return, compilerID will indicate which version of the MMX assembler
+optimizations was compiled. Currently two flavors exist: Microsoft
+Visual C++ (compilerID == 1) and GNU C (a.k.a. gcc/gas, compilerID == 2).
+On non-x86 platforms or on systems compiled without MMX optimizations, a
+value of -1 is used.
+
+Note that both png_get_asm_flagmask() and png_get_mmx_flagmask() return
+all valid, settable optimization bits for the version of the library that's
+currently in use. In the case of shared (dynamically linked) libraries,
+this may include optimizations that did not exist at the time the code was
+written and compiled. It is also possible, of course, to enable only known,
+specific optimizations; for example:
+
+ #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
+ flags = PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
+ | PNG_ASM_FLAG_MMX_READ_INTERLACE \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_UP \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+ png_set_asm_flags(png_ptr, flags);
+ #endif
+
+This method would enable only the MMX read-optimizations available at the
+time of libpng 1.2.0's release, regardless of whether a later version of
+the DLL were actually being used. (Also note that these functions did not
+exist in versions older than 1.2.0, so any attempt to run a dynamically
+linked app on such an older version would fail.)
+
+To determine whether the processor supports MMX instructions at all, use
+the png_mmx_support() function:
+
+ #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
+ mmxsupport = png_mmx_support();
+ #endif
+
+It returns -1 if MMX support is not compiled into libpng, 0 if MMX code
+is compiled but MMX is not supported by the processor, or 1 if MMX support
+is fully available. Note that png_mmx_support(), png_get_mmx_flagmask(),
+and png_get_asm_flagmask() all may be called without allocating and ini-
+tializing any PNG structures (for example, as part of a usage screen or
+"about" box).
+
+The following code can be used to prevent an application from using the
+thread_unsafe features, even if libpng was built with PNG_THREAD_UNSAFE_OK
+defined:
+
+#if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) \
+ && defined(PNG_THREAD_UNSAFE_OK)
+ /* Disable thread-unsafe features of pnggccrd */
+ if (png_access_version() >= 10200)
+ {
+ png_uint_32 mmx_disable_mask = 0;
+ png_uint_32 asm_flags;
+
+ mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
+ | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
+ asm_flags = png_get_asm_flags(png_ptr);
+ png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask);
+ }
+#endif
+
+For more extensive examples of runtime querying, enabling and disabling
+of optimized features, see contrib/gregbook/readpng2.c in the libpng
+source-code distribution.
+
+VII. MNG support
+
+The MNG specification (available at http://www.libpng.org/pub/mng) allows
+certain extensions to PNG for PNG images that are embedded in MNG datastreams.
+Libpng can support some of these extensions. To enable them, use the
+png_permit_mng_features() function:
+
+ feature_set = png_permit_mng_features(png_ptr, mask)
+ mask is a png_uint_32 containing the logical OR of the
+ features you want to enable. These include
+ PNG_FLAG_MNG_EMPTY_PLTE
+ PNG_FLAG_MNG_FILTER_64
+ PNG_ALL_MNG_FEATURES
+ feature_set is a png_uint_32 that is the logical AND of
+ your mask with the set of MNG features that is
+ supported by the version of libpng that you are using.
+
+It is an error to use this function when reading or writing a standalone
+PNG file with the PNG 8-byte signature. The PNG datastream must be wrapped
+in a MNG datastream. As a minimum, it must have the MNG 8-byte signature
+and the MHDR and MEND chunks. Libpng does not provide support for these
+or any other MNG chunks; your application must provide its own support for
+them. You may wish to consider using libmng (available at
+http://www.libmng.com) instead.
+
+VIII. Changes to Libpng from version 0.88
+
+It should be noted that versions of libpng later than 0.96 are not
+distributed by the original libpng author, Guy Schalnat, nor by
+Andreas Dilger, who had taken over from Guy during 1996 and 1997, and
+distributed versions 0.89 through 0.96, but rather by another member
+of the original PNG Group, Glenn Randers-Pehrson. Guy and Andreas are
+still alive and well, but they have moved on to other things.
+
+The old libpng functions png_read_init(), png_write_init(),
+png_info_init(), png_read_destroy(), and png_write_destroy() have been
+moved to PNG_INTERNAL in version 0.95 to discourage their use. These
+functions will be removed from libpng version 2.0.0.
+
+The preferred method of creating and initializing the libpng structures is
+via the png_create_read_struct(), png_create_write_struct(), and
+png_create_info_struct() because they isolate the size of the structures
+from the application, allow version error checking, and also allow the
+use of custom error handling routines during the initialization, which
+the old functions do not. The functions png_read_destroy() and
+png_write_destroy() do not actually free the memory that libpng
+allocated for these structs, but just reset the data structures, so they
+can be used instead of png_destroy_read_struct() and
+png_destroy_write_struct() if you feel there is too much system overhead
+allocating and freeing the png_struct for each image read.
+
+Setting the error callbacks via png_set_message_fn() before
+png_read_init() as was suggested in libpng-0.88 is no longer supported
+because this caused applications that do not use custom error functions
+to fail if the png_ptr was not initialized to zero. It is still possible
+to set the error callbacks AFTER png_read_init(), or to change them with
+png_set_error_fn(), which is essentially the same function, but with a new
+name to force compilation errors with applications that try to use the old
+method.
+
+Starting with version 1.0.7, you can find out which version of the library
+you are using at run-time:
+
+ png_uint_32 libpng_vn = png_access_version_number();
+
+The number libpng_vn is constructed from the major version, minor
+version with leading zero, and release number with leading zero,
+(e.g., libpng_vn for version 1.0.7 is 10007).
+
+You can also check which version of png.h you used when compiling your
+application:
+
+ png_uint_32 application_vn = PNG_LIBPNG_VER;
+
+IX. Y2K Compliance in libpng
+
+December 3, 2004
+
+Since the PNG Development group is an ad-hoc body, we can't make
+an official declaration.
+
+This is your unofficial assurance that libpng from version 0.71 and
+upward through 1.2.8 are Y2K compliant. It is my belief that earlier
+versions were also Y2K compliant.
+
+Libpng only has three year fields. One is a 2-byte unsigned integer that
+will hold years up to 65535. The other two hold the date in text
+format, and will hold years up to 9999.
+
+The integer is
+ "png_uint_16 year" in png_time_struct.
+
+The strings are
+ "png_charp time_buffer" in png_struct and
+ "near_time_buffer", which is a local character string in png.c.
+
+There are seven time-related functions:
+
+ png_convert_to_rfc_1123() in png.c
+ (formerly png_convert_to_rfc_1152() in error)
+ png_convert_from_struct_tm() in pngwrite.c, called
+ in pngwrite.c
+ png_convert_from_time_t() in pngwrite.c
+ png_get_tIME() in pngget.c
+ png_handle_tIME() in pngrutil.c, called in pngread.c
+ png_set_tIME() in pngset.c
+ png_write_tIME() in pngwutil.c, called in pngwrite.c
+
+All appear to handle dates properly in a Y2K environment. The
+png_convert_from_time_t() function calls gmtime() to convert from system
+clock time, which returns (year - 1900), which we properly convert to
+the full 4-digit year. There is a possibility that applications using
+libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+function, or that they are incorrectly passing only a 2-digit year
+instead of "year - 1900" into the png_convert_from_struct_tm() function,
+but this is not under our control. The libpng documentation has always
+stated that it works with 4-digit years, and the APIs have been
+documented as such.
+
+The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned
+integer to hold the year, and can hold years as large as 65535.
+
+zlib, upon which libpng depends, is also Y2K compliant. It contains
+no date-related code.
+
+
+ Glenn Randers-Pehrson
+ libpng maintainer
+ PNG Development Group
diff --git a/com32/lib/lmalloc.c b/com32/lib/lmalloc.c
index a646556c..3e69ac1d 100644
--- a/com32/lib/lmalloc.c
+++ b/com32/lib/lmalloc.c
@@ -31,19 +31,10 @@
#include <string.h>
#include <syslinux/pmapi.h>
-void *lmalloc(size_t size)
-{
- void *p;
- p = __com32.cs_pm->lmalloc(size);
- if (!p)
- errno = ENOMEM;
- return p;
-}
-
void *lzalloc(size_t size)
{
void *p;
- p = __com32.cs_pm->lmalloc(size);
+ p = lmalloc(size);
if (!p)
errno = ENOMEM;
else
@@ -53,5 +44,5 @@ void *lzalloc(size_t size)
void lfree(void *ptr)
{
- __com32.cs_pm->lfree(ptr);
+ free(ptr);
}
diff --git a/com32/lib/makeerrlist.pl b/com32/lib/makeerrlist.pl
new file mode 100644
index 00000000..9243b9dd
--- /dev/null
+++ b/com32/lib/makeerrlist.pl
@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+#
+# This creates sys_errlist from <asm/errno.h> through somewhat
+# heuristic matching. It presumes the relevant entries are of the form
+# #define Exxxx <integer> /* comment */
+#
+
+use FileHandle;
+
+%errors = ();
+%errmsg = ();
+$maxerr = -1;
+@includelist = (); # Include directories
+
+sub parse_file($) {
+ my($file) = @_;
+ my($fh) = new FileHandle;
+ my($line, $error, $msg);
+ my($kernelonly) = 0;
+ my($root);
+
+ print STDERR "opening $file\n" unless ( $quiet );
+
+ $ok = 0;
+ foreach $root ( @includelist ) {
+ if ( $fh->open($root.'//'.$file, '<') ) {
+ $ok = 1;
+ last;
+ }
+ }
+
+ if ( ! $ok ) {
+ die "$0: Cannot find file $file\n";
+ }
+
+ while ( defined($line = <$fh>) ) {
+ if ( $kernelonly ) {
+ if ( $line =~ /^\#\s*endif/ ) {
+ $kernelonly--;
+ } elsif ( $line =~ /^\#\sif/ ) {
+ $kernelonly++;
+ }
+ } else {
+ if ( $line =~ /^\#\s*define\s+([A-Z0-9_]+)\s+([0-9]+)\s*\/\*\s*(.*\S)\s*\*\// ) {
+ $error = $1;
+ $errno = $2+0;
+ $msg = $3;
+ print STDERR "$error ($errno) => \"$msg\"\n" unless ( $quiet );
+ $errors{$errno} = $error;
+ $errmsg{$errno} = $msg;
+ $maxerr = $errno if ( $errno > $maxerr );
+ } elsif ( $line =~ /^\#\s*include\s+[\<\"](.*)[\>\"]/ ) {
+ parse_file($1);
+ } elsif ( $line =~ /^\#\s*ifdef\s+__KERNEL__/ ) {
+ $kernelonly++;
+ }
+ }
+ }
+ close($fh);
+ print STDERR "closing $file\n" unless ( $quiet );
+}
+
+$v = $ENV{'KBUILD_VERBOSE'};
+$quiet = defined($v) ? !$v : 0;
+
+foreach $arg ( @ARGV ) {
+ if ( $arg eq '-q' ) {
+ $quiet = 1;
+ } elsif ( $arg =~ /^-(errlist|errnos|maxerr)$/ ) {
+ $type = $arg;
+ } elsif ( $arg =~ '^\-I' ) {
+ push(@includelist, "$'");
+ } else {
+ # Ignore
+ }
+}
+
+parse_file('errno.h');
+
+if ( $type eq '-errlist' ) {
+ print "#include <errno.h>\n";
+ printf "const int sys_nerr = %d;\n", $maxerr+1;
+ printf "const char * const sys_errlist[%d] = {\n", $maxerr+1;
+ foreach $e ( sort(keys(%errors)) ) {
+ printf " [%s] = \"%s\",\n", $errors{$e}, $errmsg{$e};
+ }
+ print "};\n";
+} elsif ( $type eq '-errnos' ) {
+ print "#include <errno.h>\n";
+ printf "const int sys_nerr = %d;\n", $maxerr+1;
+ printf "const char * const sys_errlist[%d] = {\n", $maxerr+1;
+ foreach $e ( sort(keys(%errors)) ) {
+ printf " [%s] = \"%s\",\n", $errors{$e}, $errors{$e};
+ }
+ print "};\n";
+} elsif ( $type eq '-maxerr' ) {
+ print $maxerr, "\n";
+}
diff --git a/com32/lib/malloc.c b/com32/lib/malloc.c
deleted file mode 100644
index ec103ab3..00000000
--- a/com32/lib/malloc.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * malloc.c
- *
- * Very simple linked-list based malloc()/free().
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <com32.h>
-#include <syslinux/memscan.h>
-#include "init.h"
-#include "malloc.h"
-
-struct free_arena_header __malloc_head = {
- {
- ARENA_TYPE_HEAD,
- 0,
- &__malloc_head,
- &__malloc_head,
- },
- &__malloc_head,
- &__malloc_head
-};
-
-/* This is extern so it can be overridden by the user application */
-extern size_t __stack_size;
-extern void *__mem_end; /* Produced after argv parsing */
-
-static inline size_t sp(void)
-{
- size_t sp;
- asm volatile ("movl %%esp,%0":"=rm" (sp));
- return sp;
-}
-
-#define E820_MEM_MAX 0xfff00000 /* 4 GB - 1 MB */
-
-static int consider_memory_area(void *dummy, addr_t start,
- addr_t len, bool valid)
-{
- struct free_arena_header *fp;
- addr_t end;
-
- (void)dummy;
-
- if (valid && start < E820_MEM_MAX) {
- if (len > E820_MEM_MAX - start)
- len = E820_MEM_MAX - start;
-
- end = start + len;
-
- if (end > __com32.cs_memsize) {
- if (start <= __com32.cs_memsize) {
- start = __com32.cs_memsize;
- len = end - start;
- }
-
- if (len >= 2 * sizeof(struct arena_header)) {
- fp = (struct free_arena_header *)start;
- fp->a.size = len;
- __inject_free_block(fp);
- }
- }
- }
-
- return 0;
-}
-
-static void __constructor init_memory_arena(void)
-{
- struct free_arena_header *fp;
- size_t start, total_space;
-
- start = (size_t) ARENA_ALIGN_UP(__mem_end);
- total_space = sp() - start;
-
- if (__stack_size == 0 || __stack_size > total_space >> 1)
- __stack_size = total_space >> 1; /* Half for the stack, half for the heap... */
-
- if (total_space < __stack_size + 4 * sizeof(struct arena_header))
- __stack_size = total_space - 4 * sizeof(struct arena_header);
-
- fp = (struct free_arena_header *)start;
- fp->a.size = total_space - __stack_size;
-
- __inject_free_block(fp);
-
- /* Scan the memory map to look for other suitable regions */
- if (!__com32.cs_memsize)
- return; /* Old Syslinux core, can't do this... */
-
- syslinux_scan_memory(consider_memory_area, NULL);
-}
-
-static void *__malloc_from_block(struct free_arena_header *fp, size_t size)
-{
- size_t fsize;
- struct free_arena_header *nfp, *na;
-
- fsize = fp->a.size;
-
- /* We need the 2* to account for the larger requirements of a free block */
- if (fsize >= size + 2 * sizeof(struct arena_header)) {
- /* Bigger block than required -- split block */
- nfp = (struct free_arena_header *)((char *)fp + size);
- na = fp->a.next;
-
- nfp->a.type = ARENA_TYPE_FREE;
- nfp->a.size = fsize - size;
- fp->a.type = ARENA_TYPE_USED;
- fp->a.size = size;
-
- /* Insert into all-block chain */
- nfp->a.prev = fp;
- nfp->a.next = na;
- na->a.prev = nfp;
- fp->a.next = nfp;
-
- /* Replace current block on free chain */
- nfp->next_free = fp->next_free;
- nfp->prev_free = fp->prev_free;
- fp->next_free->prev_free = nfp;
- fp->prev_free->next_free = nfp;
- } else {
- /* Allocate the whole block */
- fp->a.type = ARENA_TYPE_USED;
-
- /* Remove from free chain */
- fp->next_free->prev_free = fp->prev_free;
- fp->prev_free->next_free = fp->next_free;
- }
-
- return (void *)(&fp->a + 1);
-}
-
-void *malloc(size_t size)
-{
- struct free_arena_header *fp;
-
- if (size == 0)
- return NULL;
-
- /* Add the obligatory arena header, and round up */
- size = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
-
- for (fp = __malloc_head.next_free; fp->a.type != ARENA_TYPE_HEAD;
- fp = fp->next_free) {
- if (fp->a.size >= size) {
- /* Found fit -- allocate out of this block */
- return __malloc_from_block(fp, size);
- }
- }
-
- /* Nothing found... need to request a block from the kernel */
- return NULL; /* No kernel to get stuff from */
-}
diff --git a/com32/lib/onexit.c b/com32/lib/onexit.c
index d409e82e..272f8f1c 100644
--- a/com32/lib/onexit.c
+++ b/com32/lib/onexit.c
@@ -6,7 +6,6 @@
#include <unistd.h>
#include "atexit.h"
-extern __noreturn(*__exit_handler) (int);
static struct atexit *__atexit_list;
static __noreturn on_exit_exit(int rv)
@@ -33,7 +32,5 @@ int on_exit(void (*fctn) (int, void *), void *arg)
as->next = __atexit_list;
__atexit_list = as;
- __exit_handler = on_exit_exit;
-
return 0;
}
diff --git a/com32/lib/realloc.c b/com32/lib/realloc.c
deleted file mode 100644
index 2969e313..00000000
--- a/com32/lib/realloc.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * realloc.c
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <minmax.h>
-
-#include "malloc.h"
-
-void *realloc(void *ptr, size_t size)
-{
- struct free_arena_header *ah, *nah;
- void *newptr;
- size_t newsize, oldsize, xsize;
-
- if (!ptr)
- return malloc(size);
-
- if (size == 0) {
- free(ptr);
- return NULL;
- }
-
- ah = (struct free_arena_header *)
- ((struct arena_header *)ptr - 1);
-
- /* Actual size of the old block */
- oldsize = ah->a.size;
-
- /* Add the obligatory arena header, and round up */
- newsize = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
-
- if (oldsize >= newsize && newsize >= (oldsize >> 2) &&
- oldsize - newsize < 4096) {
- /* This allocation is close enough already. */
- return ptr;
- } else {
- xsize = oldsize;
-
- nah = ah->a.next;
- if ((char *)nah == (char *)ah + ah->a.size &&
- nah->a.type == ARENA_TYPE_FREE &&
- oldsize + nah->a.size >= newsize) {
- /* Merge in subsequent free block */
- ah->a.next = nah->a.next;
- ah->a.next->a.prev = ah;
- nah->next_free->prev_free = nah->prev_free;
- nah->prev_free->next_free = nah->next_free;
- xsize = (ah->a.size += nah->a.size);
- }
-
- if (xsize >= newsize) {
- /* We can reallocate in place */
- if (xsize >= newsize + 2 * sizeof(struct arena_header)) {
- /* Residual free block at end */
- nah = (struct free_arena_header *)((char *)ah + newsize);
- nah->a.type = ARENA_TYPE_FREE;
- nah->a.size = xsize - newsize;
- ah->a.size = newsize;
-
- /* Insert into block list */
- nah->a.next = ah->a.next;
- ah->a.next = nah;
- nah->a.next->a.prev = nah;
- nah->a.prev = ah;
-
- /* Insert into free list */
- if (newsize > oldsize) {
- /* Hack: this free block is in the path of a memory object
- which has already been grown at least once. As such, put
- it at the *end* of the freelist instead of the beginning;
- trying to save it for future realloc()s of the same block. */
- nah->prev_free = __malloc_head.prev_free;
- nah->next_free = &__malloc_head;
- __malloc_head.prev_free = nah;
- nah->prev_free->next_free = nah;
- } else {
- nah->next_free = __malloc_head.next_free;
- nah->prev_free = &__malloc_head;
- __malloc_head.next_free = nah;
- nah->next_free->prev_free = nah;
- }
- }
- /* otherwise, use up the whole block */
- return ptr;
- } else {
- /* Last resort: need to allocate a new block and copy */
- oldsize -= sizeof(struct arena_header);
- newptr = malloc(size);
- if (newptr) {
- memcpy(newptr, ptr, min(size, oldsize));
- free(ptr);
- }
- return newptr;
- }
- }
-}
diff --git a/com32/lib/strerror.c b/com32/lib/strerror.c
index 8dbe74ad..1b3d4452 100644
--- a/com32/lib/strerror.c
+++ b/com32/lib/strerror.c
@@ -6,18 +6,26 @@
char *strerror(int errnum)
{
- static char message[32] = "error "; /* enough for error 2^63-1 */
+ static char message[32] = "error "; /* enough for error 2^63-1 */
+ char numbuf[32];
+ char *p;
+ unsigned int e = (unsigned int)errnum;
- char numbuf[32];
- char *p;
+ extern const int sys_nerr;
+ extern const char *const sys_errlist[];
- p = numbuf + sizeof numbuf;
- *--p = '\0';
+ if (e < (unsigned int)sys_nerr && sys_errlist[e])
+ return (char *)sys_errlist[e];
- do {
- *--p = (errnum % 10) + '0';
- errnum /= 10;
- } while (errnum);
+ p = numbuf + sizeof numbuf;
+ *--p = '\0';
- return (char *)memcpy(message + 6, p, (numbuf + sizeof numbuf) - p);
+ do {
+ *--p = (e % 10) + '0';
+ e /= 10;
+ } while (e);
+
+ memcpy(message + 6, p, (numbuf + sizeof numbuf) - p);
+
+ return message;
}
diff --git a/com32/lib/sys/ansicon_write.c b/com32/lib/sys/ansicon_write.c
index b25f2d2e..e5483fbc 100644
--- a/com32/lib/sys/ansicon_write.c
+++ b/com32/lib/sys/ansicon_write.c
@@ -42,6 +42,7 @@
#include <syslinux/config.h>
#include "file.h"
#include "ansi.h"
+#include "graphics.h"
static void ansicon_erase(const struct term_state *, int, int, int, int);
static void ansicon_write_char(int, int, uint8_t, const struct term_state *);
@@ -90,8 +91,7 @@ int __ansicon_open(struct file_info *fp)
ti.cols = 80;
} else {
/* Force text mode */
- ireg.eax.w[0] = 0x0005;
- __intcall(0x22, &ireg, NULL);
+ syslinux_force_text_mode();
/* Initial state */
ti.rows = BIOS_ROWS ? BIOS_ROWS + 1 : 25;
diff --git a/com32/lib/sys/exit.S b/com32/lib/sys/exit.S
deleted file mode 100644
index 2ab80122..00000000
--- a/com32/lib/sys/exit.S
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Implementation of _exit() for com32 based on c32entry.S
- */
- .text
-
- .globl _Exit
- .type _Exit, @function
-_Exit:
- /* Just fall through to _exit */
- .size _Exit, .-_Exit
-
- .globl _exit
- .type _exit, @function
-_exit:
-#ifdef REGPARM
- pushl %eax
-#endif
-
- /* Run any destructors */
- movl $__dtors_start, %esi
-2:
- cmpl $__dtors_end, %esi
- jae 1f
- call *(%esi)
- addl $4,%esi
- jmp 2b
-
-1:
-#ifdef REGPARM
- popl %eax
-#else
- movl 4(%esp),%eax # Exit code in %eax = return value
-#endif
- movl (__entry_esp),%esp # Return stack pointer to entry value
- ret # Return to termination address
- .size _exit, .-_exit
-
- .data
-__exit_handler:
- .globl __exit_handler
- .long _exit
diff --git a/com32/lib/sys/file.h b/com32/lib/sys/file.h
index e984f160..f79b4f19 100644
--- a/com32/lib/sys/file.h
+++ b/com32/lib/sys/file.h
@@ -74,7 +74,7 @@ struct output_dev {
/* File structure */
-#define NFILES 32 /* Number of files to support */
+#define NFILES 128 /* Number of files to support */
#define MAXBLOCK 16384 /* Defined by ABI */
struct file_info {
@@ -98,6 +98,7 @@ struct file_info {
};
extern struct file_info __file_info[NFILES];
+extern const struct input_dev __file_dev;
/* Line input discipline */
ssize_t __line_input(struct file_info *fp, char *buf, size_t bufsize,
diff --git a/com32/lib/sys/fileclose.c b/com32/lib/sys/fileclose.c
index e2c929f2..699dbe32 100644
--- a/com32/lib/sys/fileclose.c
+++ b/com32/lib/sys/fileclose.c
@@ -34,12 +34,13 @@
#include <errno.h>
#include <com32.h>
#include <string.h>
+#include <fs.h>
#include "file.h"
int __file_close(struct file_info *fp)
{
if (fp->i.fd.handle)
- __com32.cs_pm->close_file(fp->i.fd.handle);
+ close_file(fp->i.fd.handle);
return 0;
}
diff --git a/com32/lib/sys/fileread.c b/com32/lib/sys/fileread.c
index aab99c80..26b0ceb6 100644
--- a/com32/lib/sys/fileread.c
+++ b/com32/lib/sys/fileread.c
@@ -34,6 +34,7 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <pmapi.h>
#include <syslinux/pmapi.h>
#include <minmax.h>
#include "file.h"
@@ -42,7 +43,7 @@ int __file_get_block(struct file_info *fp)
{
ssize_t bytes_read;
- bytes_read = __com32.cs_pm->read_file(&fp->i.fd.handle, fp->i.buf,
+ bytes_read = pmapi_read_file(&fp->i.fd.handle, fp->i.buf,
MAXBLOCK >> fp->i.fd.blocklg2);
if (!bytes_read) {
errno = EIO;
@@ -67,7 +68,7 @@ ssize_t __file_read(struct file_info * fp, void *buf, size_t count)
if (count > MAXBLOCK) {
/* Large transfer: copy directly, without buffering */
- ncopy = __com32.cs_pm->read_file(&fp->i.fd.handle, bufp,
+ ncopy = pmapi_read_file(&fp->i.fd.handle, bufp,
count >> fp->i.fd.blocklg2);
if (!ncopy) {
errno = EIO;
diff --git a/com32/lib/sys/gpxe.c b/com32/lib/sys/gpxe.c
index d86da42a..3cc2b845 100644
--- a/com32/lib/sys/gpxe.c
+++ b/com32/lib/sys/gpxe.c
@@ -1,13 +1,15 @@
+#include <string.h>
+
#include <sys/gpxe.h>
#include <syslinux/config.h>
-#include <string.h>
+#include <syslinux/pxe_api.h>
bool is_gpxe(void)
{
const struct syslinux_version *sv;
- com32sys_t reg;
struct s_PXENV_FILE_CHECK_API *fca;
bool gpxe;
+ int err;
sv = syslinux_version();
if (sv->filesystem != SYSLINUX_FS_PXELINUX)
@@ -16,23 +18,18 @@ bool is_gpxe(void)
fca = lzalloc(sizeof *fca);
if (!fca)
return false;
+
fca->Size = sizeof *fca;
fca->Magic = 0x91d447b2;
- memset(&reg, 0, sizeof reg);
- reg.eax.w[0] = 0x0009;
- reg.ebx.w[0] = 0x00e6; /* PXENV_FILE_API_CHECK */
- /* reg.edi.w[0] = OFFS(fca); */
- reg.es = SEG(fca);
-
- __intcall(0x22, &reg, &reg);
+ err = pxe_call(PXENV_FILE_API_CHECK, fca);
gpxe = true;
- if (reg.eflags.l & EFLAGS_CF)
+ if (err)
gpxe = false; /* Cannot invoke PXE stack */
- if (reg.eax.w[0] || fca->Status)
+ if (fca->Status)
gpxe = false; /* PXE failure */
if (fca->Magic != 0xe9c17b20)
diff --git a/com32/lib/sys/module/common.c b/com32/lib/sys/module/common.c
new file mode 100644
index 00000000..b763704e
--- /dev/null
+++ b/com32/lib/sys/module/common.c
@@ -0,0 +1,599 @@
+/*
+ * common.c
+ *
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#include <stdio.h>
+#include <elf.h>
+#include <string.h>
+#include <fs.h>
+
+#include <linux/list.h>
+#include <sys/module.h>
+
+#include "elfutils.h"
+#include "common.h"
+
+/**
+ * The one and only list of loaded modules
+ */
+LIST_HEAD(modules_head);
+
+// User-space debugging routines
+#ifdef ELF_DEBUG
+void print_elf_ehdr(Elf32_Ehdr *ehdr) {
+ int i;
+
+ fprintf(stderr, "Identification:\t");
+ for (i=0; i < EI_NIDENT; i++) {
+ printf("%d ", ehdr->e_ident[i]);
+ }
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Type:\t\t%u\n", ehdr->e_type);
+ fprintf(stderr, "Machine:\t%u\n", ehdr->e_machine);
+ fprintf(stderr, "Version:\t%u\n", ehdr->e_version);
+ fprintf(stderr, "Entry:\t\t0x%08x\n", ehdr->e_entry);
+ fprintf(stderr, "PHT Offset:\t0x%08x\n", ehdr->e_phoff);
+ fprintf(stderr, "SHT Offset:\t0x%08x\n", ehdr->e_shoff);
+ //fprintf(stderr, "Flags:\t\t%u\n", ehdr->e_flags);
+ //fprintf(stderr, "Header size:\t%u (Structure size: %u)\n", ehdr->e_ehsize,sizeof(Elf32_Ehdr));
+ fprintf(stderr, "phnum: %d shnum: %d\n", ehdr->e_phnum,
+ ehdr->e_shnum);
+}
+
+void print_elf_symbols(struct elf_module *module) {
+ unsigned int i;
+ Elf32_Sym *crt_sym;
+
+ for (i = 1; i < module->symtable_size/module->syment_size; i++)
+ {
+ crt_sym = (Elf32_Sym*)(module->sym_table + i*module->syment_size);
+
+ fprintf(stderr,"%s %d\n", module->str_table + crt_sym->st_name, crt_sym->st_value);
+
+ }
+}
+#endif //ELF_DEBUG
+
+FILE *findpath(char *name)
+{
+ struct path_entry *entry;
+ char path[FILENAME_MAX];
+ FILE *f;
+
+ f = fopen(name, "rb"); /* for full path */
+ if (f)
+ return f;
+
+ list_for_each_entry(entry, &PATH, list) {
+ bool slash = false;
+
+ /* Ensure we have a '/' separator */
+ if (entry->str[strlen(entry->str) - 1] != '/')
+ slash = true;
+
+ snprintf(path, sizeof(path), "%s%s%s",
+ entry->str, slash ? "/" : "", name);
+
+ f = fopen(path, "rb");
+ if (f)
+ return f;
+ }
+
+ return NULL;
+}
+
+/*
+ * Image files manipulation routines
+ */
+
+int image_load(struct elf_module *module)
+{
+ module->u.l._file = findpath(module->name);
+
+ if (module->u.l._file == NULL) {
+ DBG_PRINT("Could not open object file '%s'\n", module->name);
+ goto error;
+ }
+
+ module->u.l._cr_offset = 0;
+
+ return 0;
+
+error:
+ if (module->u.l._file != NULL) {
+ fclose(module->u.l._file);
+ module->u.l._file = NULL;
+ }
+
+ return -1;
+}
+
+
+int image_unload(struct elf_module *module) {
+ if (module->u.l._file != NULL) {
+ fclose(module->u.l._file);
+ module->u.l._file = NULL;
+
+ }
+ module->u.l._cr_offset = 0;
+
+ return 0;
+}
+
+int image_read(void *buff, size_t size, struct elf_module *module) {
+ size_t result = fread(buff, size, 1, module->u.l._file);
+
+ if (result < 1)
+ return -1;
+
+ module->u.l._cr_offset += size;
+ return 0;
+}
+
+int image_skip(size_t size, struct elf_module *module) {
+ void *skip_buff = NULL;
+ size_t result;
+
+ if (size == 0)
+ return 0;
+
+ skip_buff = malloc(size);
+ result = fread(skip_buff, size, 1, module->u.l._file);
+ free(skip_buff);
+
+ if (result < 1)
+ return -1;
+
+ module->u.l._cr_offset += size;
+ return 0;
+}
+
+int image_seek(Elf32_Off offset, struct elf_module *module) {
+ if (offset < module->u.l._cr_offset) // Cannot seek backwards
+ return -1;
+
+ return image_skip(offset - module->u.l._cr_offset, module);
+}
+
+
+// Initialization of the module subsystem
+int modules_init(void) {
+ return 0;
+}
+
+// Termination of the module subsystem
+void modules_term(void) {
+
+}
+
+// Allocates the structure for a new module
+struct elf_module *module_alloc(const char *name) {
+ struct elf_module *result = malloc(sizeof(struct elf_module));
+
+ if (!result) {
+ dprintf("module: Failed to alloc elf_module\n");
+ return NULL;
+ }
+
+ memset(result, 0, sizeof(struct elf_module));
+
+ INIT_LIST_HEAD(&result->list);
+ INIT_LIST_HEAD(&result->required);
+ INIT_LIST_HEAD(&result->dependants);
+
+ strncpy(result->name, name, MODULE_NAME_SIZE);
+
+ return result;
+}
+
+struct module_dep *module_dep_alloc(struct elf_module *module) {
+ struct module_dep *result = malloc(sizeof(struct module_dep));
+
+ INIT_LIST_HEAD (&result->list);
+
+ result->module = module;
+
+ return result;
+}
+
+struct elf_module *module_find(const char *name) {
+ struct elf_module *cr_module;
+
+ for_each_module(cr_module) {
+ if (strcmp(cr_module->name, name) == 0)
+ return cr_module;
+ }
+
+ return NULL;
+}
+
+
+// Performs verifications on ELF header to assure that the open file is a
+// valid SYSLINUX ELF module.
+int check_header_common(Elf32_Ehdr *elf_hdr) {
+ // Check the header magic
+ if (elf_hdr->e_ident[EI_MAG0] != ELFMAG0 ||
+ elf_hdr->e_ident[EI_MAG1] != ELFMAG1 ||
+ elf_hdr->e_ident[EI_MAG2] != ELFMAG2 ||
+ elf_hdr->e_ident[EI_MAG3] != ELFMAG3) {
+
+ DBG_PRINT("The file is not an ELF object\n");
+ return -1;
+ }
+
+ if (elf_hdr->e_ident[EI_CLASS] != MODULE_ELF_CLASS) {
+ DBG_PRINT("Invalid ELF class code\n");
+ return -1;
+ }
+
+ if (elf_hdr->e_ident[EI_DATA] != MODULE_ELF_DATA) {
+ DBG_PRINT("Invalid ELF data encoding\n");
+ return -1;
+ }
+
+ if (elf_hdr->e_ident[EI_VERSION] != MODULE_ELF_VERSION ||
+ elf_hdr->e_version != MODULE_ELF_VERSION) {
+ DBG_PRINT("Invalid ELF file version\n");
+ return -1;
+ }
+
+ if (elf_hdr->e_machine != MODULE_ELF_MACHINE) {
+ DBG_PRINT("Invalid ELF architecture\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int enforce_dependency(struct elf_module *req, struct elf_module *dep) {
+ struct module_dep *crt_dep;
+ struct module_dep *new_dep;
+
+ list_for_each_entry(crt_dep, &req->dependants, list) {
+ if (crt_dep->module == dep) {
+ // The dependency is already enforced
+ return 0;
+ }
+ }
+
+ new_dep = module_dep_alloc(req);
+ list_add(&new_dep->list, &dep->required);
+
+ new_dep = module_dep_alloc(dep);
+ list_add(&new_dep->list, &req->dependants);
+
+ return 0;
+}
+
+int clear_dependency(struct elf_module *req, struct elf_module *dep) {
+ struct module_dep *crt_dep = NULL;
+ int found = 0;
+
+ list_for_each_entry(crt_dep, &req->dependants, list) {
+ if (crt_dep->module == dep) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ list_del(&crt_dep->list);
+ free(crt_dep);
+ }
+
+ found = 0;
+
+ list_for_each_entry(crt_dep, &dep->required, list) {
+ if (crt_dep->module == req) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ list_del(&crt_dep->list);
+ free(crt_dep);
+ }
+
+ return 0;
+}
+
+int check_symbols(struct elf_module *module)
+{
+ unsigned int i;
+ Elf32_Sym *crt_sym = NULL, *ref_sym = NULL;
+ char *crt_name;
+ struct elf_module *crt_module;
+
+ int strong_count;
+ int weak_count;
+
+ for (i = 1; i < module->symtable_size/module->syment_size; i++)
+ {
+ crt_sym = symbol_get_entry(module, i);
+ crt_name = module->str_table + crt_sym->st_name;
+
+ strong_count = 0;
+ weak_count = (ELF32_ST_BIND(crt_sym->st_info) == STB_WEAK);
+
+ for_each_module(crt_module)
+ {
+ ref_sym = module_find_symbol(crt_name, crt_module);
+
+ // If we found a definition for our symbol...
+ if (ref_sym != NULL && ref_sym->st_shndx != SHN_UNDEF)
+ {
+ switch (ELF32_ST_BIND(ref_sym->st_info))
+ {
+ case STB_GLOBAL:
+ strong_count++;
+ break;
+ case STB_WEAK:
+ weak_count++;
+ break;
+ }
+ }
+ }
+
+ if (crt_sym->st_shndx == SHN_UNDEF)
+ {
+ // We have an undefined symbol
+ //
+ // We use the weak_count to differentiate
+ // between Syslinux-derivative-specific
+ // functions. For example, unload_pxe() is
+ // only provided by PXELINUX, so we mark it as
+ // __weak and replace it with a reference to
+ // undefined_symbol() on SYSLINUX, EXTLINUX,
+ // and ISOLINUX. See perform_relocations().
+ if (strong_count == 0 && weak_count == 0)
+ {
+ DBG_PRINT("Symbol %s is undefined\n", crt_name);
+ printf("Undef symbol FAIL: %s\n",crt_name);
+ return -1;
+ }
+ }
+ else
+ {
+ if (strong_count > 0 && ELF32_ST_BIND(ref_sym->st_info) == STB_GLOBAL)
+ {
+ // It's not an error - at relocation, the most recent symbol
+ // will be considered
+ DBG_PRINT("Info: Symbol %s is defined more than once\n", crt_name);
+ }
+ }
+ //printf("symbol %s laoded from %d\n",crt_name,crt_sym->st_value);
+ }
+
+ return 0;
+}
+
+int module_unloadable(struct elf_module *module) {
+ if (!list_empty(&module->dependants))
+ return 0;
+
+ return 1;
+}
+
+
+// Unloads the module from the system and releases all the associated memory
+int _module_unload(struct elf_module *module) {
+ struct module_dep *crt_dep, *tmp;
+ // Make sure nobody needs us
+ if (!module_unloadable(module)) {
+ DBG_PRINT("Module is required by other modules.\n");
+ return -1;
+ }
+
+ // Remove any dependency information
+ list_for_each_entry_safe(crt_dep, tmp, &module->required, list) {
+ clear_dependency(crt_dep->module, module);
+ }
+
+ // Remove the module from the module list
+ list_del_init(&module->list);
+
+ // Release the loaded segments or sections
+ if (module->module_addr != NULL) {
+ elf_free(module->module_addr);
+
+ DBG_PRINT("%s MODULE %s UNLOADED\n", module->shallow ? "SHALLOW" : "",
+ module->name);
+ }
+ // Release the module structure
+ free(module);
+
+ return 0;
+}
+
+int module_unload(struct elf_module *module) {
+ module_ctor_t *dtor;
+
+ for (dtor = module->dtors; dtor && *dtor; dtor++)
+ (*dtor) ();
+
+ return _module_unload(module);
+}
+
+struct elf_module *unload_modules_since(const char *name) {
+ struct elf_module *m, *mod, *begin = NULL;
+
+ for_each_module(mod) {
+ if (!strcmp(mod->name, name)) {
+ begin = mod;
+ break;
+ }
+ }
+
+ if (!begin)
+ return begin;
+
+ for_each_module_safe(mod, m) {
+ if (mod == begin)
+ break;
+
+ if (mod != begin)
+ module_unload(mod);
+ }
+
+ return begin;
+}
+
+static Elf32_Sym *module_find_symbol_sysv(const char *name, struct elf_module *module) {
+ unsigned long h = elf_hash((const unsigned char*)name);
+ Elf32_Word *cr_word = module->hash_table;
+
+ Elf32_Word nbucket = *cr_word++;
+ cr_word++; // Skip nchain
+
+ Elf32_Word *bkt = cr_word;
+ Elf32_Word *chn = cr_word + nbucket;
+
+ Elf32_Word crt_index = bkt[h % module->hash_table[0]];
+ Elf32_Sym *crt_sym;
+
+
+ while (crt_index != STN_UNDEF) {
+ crt_sym = symbol_get_entry(module, crt_index);
+
+ if (strcmp(name, module->str_table + crt_sym->st_name) == 0)
+ return crt_sym;
+
+ crt_index = chn[crt_index];
+ }
+
+ return NULL;
+}
+
+static Elf32_Sym *module_find_symbol_gnu(const char *name, struct elf_module *module) {
+ unsigned long h = elf_gnu_hash((const unsigned char*)name);
+
+ // Setup code (TODO: Optimize this by computing only once)
+ Elf32_Word *cr_word = module->ghash_table;
+ Elf32_Word nbucket = *cr_word++;
+ Elf32_Word symbias = *cr_word++;
+ Elf32_Word bitmask_nwords = *cr_word++;
+
+ if ((bitmask_nwords & (bitmask_nwords - 1)) != 0) {
+ DBG_PRINT("Invalid GNU Hash structure\n");
+ return NULL;
+ }
+
+ Elf32_Word gnu_shift = *cr_word++;
+
+ Elf32_Addr *gnu_bitmask = (Elf32_Addr*)cr_word;
+ cr_word += MODULE_ELF_CLASS_SIZE / 32 * bitmask_nwords;
+
+ Elf32_Word *gnu_buckets = cr_word;
+ cr_word += nbucket;
+
+ Elf32_Word *gnu_chain_zero = cr_word - symbias;
+
+ // Computations
+ Elf32_Word bitmask_word = gnu_bitmask[(h / MODULE_ELF_CLASS_SIZE) &
+ (bitmask_nwords - 1)];
+
+ unsigned int hashbit1 = h & (MODULE_ELF_CLASS_SIZE - 1);
+ unsigned int hashbit2 = (h >> gnu_shift) & (MODULE_ELF_CLASS_SIZE - 1);
+
+ if ((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1) {
+ unsigned long rem;
+ Elf32_Word bucket;
+
+ rem = h % nbucket;
+
+ bucket = gnu_buckets[rem];
+
+ if (bucket != 0) {
+ const Elf32_Word* hasharr = &gnu_chain_zero[bucket];
+
+ do {
+ if (((*hasharr ^ h ) >> 1) == 0) {
+ Elf32_Sym *crt_sym = symbol_get_entry(module, (hasharr - gnu_chain_zero));
+
+ if (strcmp(name, module->str_table + crt_sym->st_name) == 0) {
+ return crt_sym;
+ }
+ }
+ } while ((*hasharr++ & 1u) == 0);
+ }
+ }
+
+ return NULL;
+}
+
+static Elf32_Sym *module_find_symbol_iterate(const char *name,struct elf_module *module)
+{
+
+ unsigned int i;
+ Elf32_Sym *crt_sym;
+
+ for (i = 1; i < module->symtable_size/module->syment_size; i++)
+ {
+ crt_sym = symbol_get_entry(module, i);
+ if (strcmp(name, module->str_table + crt_sym->st_name) == 0)
+ {
+ return crt_sym;
+ }
+ }
+
+ return NULL;
+}
+
+Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module) {
+ Elf32_Sym *result = NULL;
+
+ if (module->ghash_table != NULL)
+ result = module_find_symbol_gnu(name, module);
+
+ if (result == NULL)
+ {
+ if (module->hash_table != NULL)
+ {
+ //printf("Attempting SYSV Symbol search\n");
+ result = module_find_symbol_sysv(name, module);
+ }
+ else
+ {
+ //printf("Attempting Iterative Symbol search\n");
+ result = module_find_symbol_iterate(name, module);
+ }
+ }
+
+ return result;
+}
+
+Elf32_Sym *global_find_symbol(const char *name, struct elf_module **module) {
+ struct elf_module *crt_module;
+ Elf32_Sym *crt_sym = NULL;
+ Elf32_Sym *result = NULL;
+
+ for_each_module(crt_module) {
+ crt_sym = module_find_symbol(name, crt_module);
+
+ if (crt_sym != NULL && crt_sym->st_shndx != SHN_UNDEF) {
+ switch (ELF32_ST_BIND(crt_sym->st_info)) {
+ case STB_GLOBAL:
+ if (module != NULL) {
+ *module = crt_module;
+ }
+ return crt_sym;
+ case STB_WEAK:
+ // Consider only the first weak symbol
+ if (result == NULL) {
+ if (module != NULL) {
+ *module = crt_module;
+ }
+ result = crt_sym;
+ }
+ break;
+ }
+ }
+ }
+
+ return result;
+}
diff --git a/com32/lib/sys/module/common.h b/com32/lib/sys/module/common.h
new file mode 100644
index 00000000..54f0ec4b
--- /dev/null
+++ b/com32/lib/sys/module/common.h
@@ -0,0 +1,73 @@
+/*
+ * common.h - Common internal operations performed by the module subsystem
+ *
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#ifndef COMMON_H_
+#define COMMON_H_
+
+#include <stdio.h>
+
+#include <sys/module.h>
+#include <linux/list.h>
+
+#include "elfutils.h"
+
+
+// Performs an operation and jumps to a given label if an error occurs
+#define CHECKED(res, expr, error) \
+ do { \
+ (res) = (expr); \
+ if ((res) < 0) \
+ goto error; \
+ } while (0)
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+static inline Elf32_Sym *symbol_get_entry(struct elf_module *module, int entry)
+{
+ char *sym_table = (char *)module->sym_table;
+ int index = entry * module->syment_size;
+
+ return (Elf32_Sym *)(sym_table + index);
+}
+
+//#define ELF_DEBUG
+
+#ifdef ELF_DEBUG
+#define DBG_PRINT(fmt, args...) fprintf(stderr, "[ELF] " fmt, ##args)
+#else
+#define DBG_PRINT(fmt, args...) // Expand to nothing
+#endif
+
+// User-space debugging routines
+#ifdef ELF_DEBUG
+extern void print_elf_ehdr(Elf32_Ehdr *ehdr);
+extern void print_elf_symbols(struct elf_module *module);
+#endif //ELF_DEBUG
+
+
+/*
+ * Image files manipulation routines
+ */
+
+extern int image_load(struct elf_module *module);
+extern int image_unload(struct elf_module *module);
+extern int image_read(void *buff, size_t size, struct elf_module *module);
+extern int image_skip(size_t size, struct elf_module *module);
+extern int image_seek(Elf32_Off offset, struct elf_module *module);
+
+extern struct module_dep *module_dep_alloc(struct elf_module *module);
+
+extern int check_header_common(Elf32_Ehdr *elf_hdr);
+
+extern int enforce_dependency(struct elf_module *req, struct elf_module *dep);
+extern int clear_dependency(struct elf_module *req, struct elf_module *dep);
+
+extern int check_symbols(struct elf_module *module);
+
+
+#endif /* COMMON_H_ */
diff --git a/com32/lib/sys/module/elf_module.c b/com32/lib/sys/module/elf_module.c
new file mode 100644
index 00000000..6a540273
--- /dev/null
+++ b/com32/lib/sys/module/elf_module.c
@@ -0,0 +1,631 @@
+/*
+ * elf_module.c
+ *
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <elf.h>
+#include <dprintf.h>
+#include <core.h>
+
+#include <linux/list.h>
+#include <sys/module.h>
+#include <sys/exec.h>
+
+#include "elfutils.h"
+#include "common.h"
+
+static int check_header(Elf32_Ehdr *elf_hdr) {
+ int res;
+
+ res = check_header_common(elf_hdr);
+
+ if (res != 0)
+ return res;
+
+ if (elf_hdr->e_type != MODULE_ELF_TYPE) {
+ DBG_PRINT("The ELF file must be a shared object\n");
+ return -1;
+ }
+
+ if (elf_hdr->e_phoff == 0x00000000) {
+ DBG_PRINT("PHT missing\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * The implementation assumes that the loadable segments are present
+ * in the PHT sorted by their offsets, so that only forward seeks would
+ * be necessary.
+ */
+static int load_segments(struct elf_module *module, Elf32_Ehdr *elf_hdr) {
+ int i;
+ int res = 0;
+ char *pht = NULL;
+ char *sht = NULL;
+ Elf32_Phdr *cr_pht;
+ Elf32_Shdr *cr_sht;
+
+ Elf32_Addr min_addr = 0x00000000; // Min. ELF vaddr
+ Elf32_Addr max_addr = 0x00000000; // Max. ELF vaddr
+ Elf32_Word max_align = sizeof(void*); // Min. align of posix_memalign()
+ Elf32_Addr min_alloc, max_alloc; // Min. and max. aligned allocables
+
+ Elf32_Addr dyn_addr = 0x00000000;
+
+ // Get to the PHT
+ image_seek(elf_hdr->e_phoff, module);
+
+ // Load the PHT
+ pht = malloc(elf_hdr->e_phnum * elf_hdr->e_phentsize);
+ if (!pht)
+ return -1;
+
+ image_read(pht, elf_hdr->e_phnum * elf_hdr->e_phentsize, module);
+
+ // Compute the memory needings of the module
+ for (i=0; i < elf_hdr->e_phnum; i++) {
+ cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize);
+
+ switch (cr_pht->p_type) {
+ case PT_LOAD:
+ if (i == 0) {
+ min_addr = cr_pht->p_vaddr;
+ } else {
+ min_addr = MIN(min_addr, cr_pht->p_vaddr);
+ }
+
+ max_addr = MAX(max_addr, cr_pht->p_vaddr + cr_pht->p_memsz);
+ max_align = MAX(max_align, cr_pht->p_align);
+ break;
+ case PT_DYNAMIC:
+ dyn_addr = cr_pht->p_vaddr;
+ break;
+ default:
+ // Unsupported - ignore
+ break;
+ }
+ }
+
+ if (max_addr - min_addr == 0) {
+ // No loadable segments
+ DBG_PRINT("No loadable segments found\n");
+ goto out;
+ }
+
+ if (dyn_addr == 0) {
+ DBG_PRINT("No dynamic information segment found\n");
+ goto out;
+ }
+
+ // The minimum address that should be allocated
+ min_alloc = min_addr - (min_addr % max_align);
+
+ // The maximum address that should be allocated
+ max_alloc = max_addr - (max_addr % max_align);
+ if (max_addr % max_align > 0)
+ max_alloc += max_align;
+
+
+ if (elf_malloc(&module->module_addr,
+ max_align,
+ max_alloc-min_alloc) != 0) {
+
+ DBG_PRINT("Could not allocate segments\n");
+ goto out;
+ }
+
+ module->base_addr = (Elf32_Addr)(module->module_addr) - min_alloc;
+ module->module_size = max_alloc - min_alloc;
+
+ // Zero-initialize the memory
+ memset(module->module_addr, 0, module->module_size);
+
+ for (i = 0; i < elf_hdr->e_phnum; i++) {
+ cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize);
+
+ if (cr_pht->p_type == PT_LOAD) {
+ // Copy the segment at its destination
+ if (cr_pht->p_offset < module->u.l._cr_offset) {
+ // The segment contains data before the current offset
+ // It can be discarded without worry - it would contain only
+ // headers
+ Elf32_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset;
+
+ if (image_read((char *)module_get_absolute(cr_pht->p_vaddr, module) + aux_off,
+ cr_pht->p_filesz - aux_off, module) < 0) {
+ res = -1;
+ goto out;
+ }
+ } else {
+ if (image_seek(cr_pht->p_offset, module) < 0) {
+ res = -1;
+ goto out;
+ }
+
+ if (image_read(module_get_absolute(cr_pht->p_vaddr, module),
+ cr_pht->p_filesz, module) < 0) {
+ res = -1;
+ goto out;
+ }
+ }
+
+ /*
+ DBG_PRINT("Loadable segment of size 0x%08x copied from vaddr 0x%08x at 0x%08x\n",
+ cr_pht->p_filesz,
+ cr_pht->p_vaddr,
+ (Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module));
+ */
+ }
+ }
+
+ // Get to the SHT
+ image_seek(elf_hdr->e_shoff, module);
+
+ // Load the SHT
+ sht = malloc(elf_hdr->e_shnum * elf_hdr->e_shentsize);
+ if (!sht) {
+ res = -1;
+ goto out;
+ }
+
+ image_read(sht, elf_hdr->e_shnum * elf_hdr->e_shentsize, module);
+
+ // Setup the symtable size
+ for (i = 0; i < elf_hdr->e_shnum; i++) {
+ cr_sht = (Elf32_Shdr*)(sht + i * elf_hdr->e_shentsize);
+
+ if (cr_sht->sh_type == SHT_DYNSYM) {
+ module->symtable_size = cr_sht->sh_size;
+ break;
+ }
+ }
+
+ free(sht);
+
+ // Setup dynamic segment location
+ module->dyn_table = module_get_absolute(dyn_addr, module);
+
+ /*
+ DBG_PRINT("Base address: 0x%08x, aligned at 0x%08x\n", module->base_addr,
+ max_align);
+ DBG_PRINT("Module size: 0x%08x\n", module->module_size);
+ */
+
+out:
+ // Free up allocated memory
+ if (pht != NULL)
+ free(pht);
+
+ return res;
+}
+
+static int prepare_dynlinking(struct elf_module *module) {
+ Elf32_Dyn *dyn_entry = module->dyn_table;
+
+ while (dyn_entry->d_tag != DT_NULL) {
+ switch (dyn_entry->d_tag) {
+ case DT_NEEDED:
+ /*
+ * It's unlikely there'll be more than
+ * MAX_NR_DEPS DT_NEEDED entries but if there
+ * are then inform the user that we ran out of
+ * space.
+ */
+ if (module->nr_needed < MAX_NR_DEPS)
+ module->needed[module->nr_needed++] = dyn_entry->d_un.d_ptr;
+ else {
+ printf("Too many dependencies!\n");
+ return -1;
+ }
+ break;
+ case DT_HASH:
+ module->hash_table =
+ (Elf32_Word*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+ case DT_GNU_HASH:
+ module->ghash_table =
+ (Elf32_Word*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+ case DT_STRTAB:
+ module->str_table =
+ (char*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+ case DT_SYMTAB:
+ module->sym_table =
+ module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+ case DT_STRSZ:
+ module->strtable_size = dyn_entry->d_un.d_val;
+ break;
+ case DT_SYMENT:
+ module->syment_size = dyn_entry->d_un.d_val;
+ break;
+ case DT_PLTGOT: // The first entry in the GOT
+ module->got = module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+ }
+
+ dyn_entry++;
+ }
+
+ return 0;
+}
+
+void undefined_symbol(void)
+{
+ printf("Error: An undefined symbol was referenced\n");
+ kaboom();
+}
+
+static int perform_relocation(struct elf_module *module, Elf32_Rel *rel) {
+ Elf32_Word *dest = module_get_absolute(rel->r_offset, module);
+
+ // The symbol reference index
+ Elf32_Word sym = ELF32_R_SYM(rel->r_info);
+ unsigned char type = ELF32_R_TYPE(rel->r_info);
+
+ // The symbol definition (if applicable)
+ Elf32_Sym *sym_def = NULL;
+ struct elf_module *sym_module = NULL;
+ Elf32_Addr sym_addr = 0x0;
+
+ if (sym > 0) {
+ // Find out details about the symbol
+
+ // The symbol reference
+ Elf32_Sym *sym_ref = symbol_get_entry(module, sym);
+
+ // The symbol definition
+ sym_def =
+ global_find_symbol(module->str_table + sym_ref->st_name,
+ &sym_module);
+
+ if (sym_def == NULL) {
+ DBG_PRINT("Cannot perform relocation for symbol %s\n",
+ module->str_table + sym_ref->st_name);
+
+ if (ELF32_ST_BIND(sym_ref->st_info) != STB_WEAK)
+ return -1;
+
+ // This must be a derivative-specific
+ // function. We're OK as long as we never
+ // execute the function.
+ sym_def = global_find_symbol("undefined_symbol", &sym_module);
+ }
+
+ // Compute the absolute symbol virtual address
+ sym_addr = (Elf32_Addr)module_get_absolute(sym_def->st_value, sym_module);
+
+ if (sym_module != module) {
+ // Create a dependency
+ enforce_dependency(sym_module, module);
+ }
+ }
+
+ switch (type) {
+ case R_386_NONE:
+ // Do nothing
+ break;
+ case R_386_32:
+ *dest += sym_addr;
+ break;
+ case R_386_PC32:
+ *dest += sym_addr - (Elf32_Addr)dest;
+ break;
+ case R_386_COPY:
+ if (sym_addr > 0) {
+ memcpy((void*)dest, (void*)sym_addr, sym_def->st_size);
+ }
+ break;
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ // Maybe TODO: Keep track of the GOT entries allocations
+ *dest = sym_addr;
+ break;
+ case R_386_RELATIVE:
+ *dest += module->base_addr;
+ break;
+ default:
+ DBG_PRINT("Relocation type %d not supported\n", type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int resolve_symbols(struct elf_module *module) {
+ Elf32_Dyn *dyn_entry = module->dyn_table;
+ unsigned int i;
+ int res;
+
+ Elf32_Word plt_rel_size = 0;
+ char *plt_rel = NULL;
+
+ char *rel = NULL;
+ Elf32_Word rel_size = 0;
+ Elf32_Word rel_entry = 0;
+
+ // The current relocation
+ Elf32_Rel *crt_rel;
+
+ while (dyn_entry->d_tag != DT_NULL) {
+ switch(dyn_entry->d_tag) {
+
+ // PLT relocation information
+ case DT_PLTRELSZ:
+ plt_rel_size = dyn_entry->d_un.d_val;
+ break;
+ case DT_PLTREL:
+ if (dyn_entry->d_un.d_val != DT_REL) {
+ DBG_PRINT("Unsupported PLT relocation\n");
+ return -1;
+ }
+ case DT_JMPREL:
+ plt_rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+
+ // Standard relocation information
+ case DT_REL:
+ rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
+ break;
+ case DT_RELSZ:
+ rel_size = dyn_entry->d_un.d_val;
+ break;
+ case DT_RELENT:
+ rel_entry = dyn_entry->d_un.d_val;
+ break;
+
+ // Module initialization and termination
+ case DT_INIT:
+ // TODO Implement initialization functions
+ break;
+ case DT_FINI:
+ // TODO Implement finalization functions
+ break;
+ }
+
+ dyn_entry++;
+ }
+
+ if (rel_size > 0) {
+ // Process standard relocations
+ for (i = 0; i < rel_size/rel_entry; i++) {
+ crt_rel = (Elf32_Rel*)(rel + i*rel_entry);
+
+ res = perform_relocation(module, crt_rel);
+
+ if (res < 0)
+ return res;
+ }
+
+ }
+
+ if (plt_rel_size > 0) {
+ // TODO: Permit this lazily
+ // Process PLT relocations
+ for (i = 0; i < plt_rel_size/sizeof(Elf32_Rel); i++) {
+ crt_rel = (Elf32_Rel*)(plt_rel + i*sizeof(Elf32_Rel));
+
+ res = perform_relocation(module, crt_rel);
+
+ if (res < 0)
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+static int extract_operations(struct elf_module *module) {
+ Elf32_Sym *ctors_start, *ctors_end;
+ Elf32_Sym *dtors_start, *dtors_end;
+ module_ctor_t *ctors = NULL;
+ module_ctor_t *dtors = NULL;
+
+ ctors_start = module_find_symbol("__ctors_start", module);
+ ctors_end = module_find_symbol("__ctors_end", module);
+
+ if (ctors_start && ctors_end) {
+ module_ctor_t *start, *end;
+ int nr_ctors = 0;
+ int i, size;
+
+ start = module_get_absolute(ctors_start->st_value, module);
+ end = module_get_absolute(ctors_end->st_value, module);
+
+ nr_ctors = end - start;
+
+ size = nr_ctors * sizeof(module_ctor_t);
+ size += sizeof(module_ctor_t); /* NULL entry */
+
+ ctors = malloc(size);
+ if (!ctors) {
+ printf("Unable to alloc memory for ctors\n");
+ return -1;
+ }
+
+ memset(ctors, 0, size);
+ for (i = 0; i < nr_ctors; i++)
+ ctors[i] = start[i];
+
+ module->ctors = ctors;
+ }
+
+ dtors_start = module_find_symbol("__dtors_start", module);
+ dtors_end = module_find_symbol("__dtors_end", module);
+
+ if (dtors_start && dtors_end) {
+ module_ctor_t *start, *end;
+ int nr_dtors = 0;
+ int i, size;
+
+ start = module_get_absolute(dtors_start->st_value, module);
+ end = module_get_absolute(dtors_end->st_value, module);
+
+ nr_dtors = end - start;
+
+ size = nr_dtors * sizeof(module_ctor_t);
+ size += sizeof(module_ctor_t); /* NULL entry */
+
+ dtors = malloc(size);
+ if (!dtors) {
+ printf("Unable to alloc memory for dtors\n");
+ free(ctors);
+ return -1;
+ }
+
+ memset(dtors, 0, size);
+ for (i = 0; i < nr_dtors; i++)
+ dtors[i] = start[i];
+
+ module->dtors = dtors;
+ }
+
+ return 0;
+}
+
+// Loads the module into the system
+int module_load(struct elf_module *module) {
+ int res;
+ Elf32_Sym *main_sym;
+ Elf32_Ehdr elf_hdr;
+ module_ctor_t *ctor;
+ struct elf_module *head = NULL;
+
+ // Do not allow duplicate modules
+ if (module_find(module->name) != NULL) {
+ DBG_PRINT("Module %s is already loaded.\n", module->name);
+ return EEXIST;
+ }
+
+ // Get a mapping/copy of the ELF file in memory
+ res = image_load(module);
+
+ if (res < 0) {
+ return res;
+ }
+
+ // The module is a fully featured dynamic library
+ module->shallow = 0;
+
+ CHECKED(res, image_read(&elf_hdr, sizeof(Elf32_Ehdr), module), error);
+ //printf("check... 1\n");
+
+ //print_elf_ehdr(&elf_hdr);
+
+ // Checking the header signature and members
+ CHECKED(res, check_header(&elf_hdr), error);
+ //printf("check... 2\n");
+
+ // Load the segments in the memory
+ CHECKED(res, load_segments(module, &elf_hdr), error);
+ //printf("bleah... 3\n");
+ // Obtain dynamic linking information
+ CHECKED(res, prepare_dynlinking(module), error);
+ //printf("check... 4\n");
+
+ head = module_current();
+
+ /* Find modules we need to load as dependencies */
+ if (module->str_table) {
+ int i;
+
+ /*
+ * Note that we have to load the dependencies in
+ * reverse order.
+ */
+ for (i = module->nr_needed - 1; i >= 0; i--) {
+ char *dep, *p;
+ char *argv[2] = { NULL, NULL };
+
+ dep = module->str_table + module->needed[i];
+
+ /* strip everything but the last component */
+ if (!strlen(dep))
+ continue;
+
+ if (strchr(dep, '/')) {
+ p = strrchr(dep, '/');
+ p++;
+ } else
+ p = dep;
+
+ argv[0] = p;
+ res = spawn_load(p, 1, argv);
+ if (res < 0) {
+ printf("Failed to load %s\n", p);
+ goto error;
+ }
+ }
+ }
+
+ // Check the symbols for duplicates / missing definitions
+ CHECKED(res, check_symbols(module), error);
+ //printf("check... 5\n");
+
+ main_sym = module_find_symbol("main", module);
+ if (main_sym)
+ module->main_func =
+ module_get_absolute(main_sym->st_value, module);
+
+ //printf("check... 6\n");
+
+ // Add the module at the beginning of the module list
+ list_add(&module->list, &modules_head);
+
+ // Perform the relocations
+ resolve_symbols(module);
+
+ // Obtain constructors and destructors
+ CHECKED(res, extract_operations(module), error);
+
+ //dprintf("module->symtable_size = %d\n", module->symtable_size);
+
+ //print_elf_symbols(module);
+
+ // The file image is no longer needed
+ image_unload(module);
+
+ /*
+ DBG_PRINT("MODULE %s LOADED SUCCESSFULLY (main@%p, init@%p, exit@%p)\n",
+ module->name,
+ (module->main_func == NULL) ? NULL : *(module->main_func),
+ (module->init_func == NULL) ? NULL : *(module->init_func),
+ (module->exit_func == NULL) ? NULL : *(module->exit_func));
+ */
+
+ for (ctor = module->ctors; ctor && *ctor; ctor++)
+ (*ctor) ();
+
+ return 0;
+
+error:
+ if (head)
+ unload_modules_since(head->name);
+
+ // Remove the module from the module list (if applicable)
+ list_del_init(&module->list);
+
+ if (module->module_addr != NULL) {
+ elf_free(module->module_addr);
+ module->module_addr = NULL;
+ }
+
+ image_unload(module);
+
+ // Clear the execution part of the module buffer
+ memset(&module->u, 0, sizeof module->u);
+
+ return res;
+}
+
diff --git a/com32/lib/sys/module/elfutils.c b/com32/lib/sys/module/elfutils.c
new file mode 100644
index 00000000..b7d760b4
--- /dev/null
+++ b/com32/lib/sys/module/elfutils.c
@@ -0,0 +1,89 @@
+#include <stdlib.h>
+#include <errno.h>
+
+#include "elfutils.h"
+
+unsigned long elf_hash(const unsigned char *name) {
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ if ((g = h & 0xF0000000))
+ h ^= g >> 24;
+
+ h &= ~g;
+ }
+
+ return h;
+}
+
+unsigned long elf_gnu_hash(const unsigned char *name) {
+ unsigned long h = 5381;
+ unsigned char c;
+
+ for (c = *name; c != '\0'; c = *++name) {
+ h = h * 33 + c;
+ }
+
+ return h & 0xFFFFFFFF;
+}
+
+#ifndef HAVE_ELF_POSIX_MEMALIGN
+
+struct memalign_info {
+ void *start_addr;
+ char data[0];
+};
+
+int elf_malloc(void **memptr, size_t alignment, size_t size) {
+ char *start_addr = NULL;
+ struct memalign_info *info;
+
+ if ((alignment & (alignment - 1)) != 0)
+ return EINVAL;
+ if (alignment % sizeof(void*) != 0)
+ alignment = sizeof(void*);
+
+ start_addr = malloc(size + (alignment > sizeof(struct memalign_info) ?
+ alignment : sizeof(struct memalign_info)));
+
+ if (start_addr == NULL)
+ return ENOMEM;
+
+
+ info = (struct memalign_info*)(start_addr -
+ ((unsigned long)start_addr % alignment) +
+ alignment - sizeof(struct memalign_info));
+
+ info->start_addr = start_addr;
+
+ *memptr = info->data;
+
+ return 0;
+}
+
+void elf_free(char *memptr) {
+ struct memalign_info *info = (struct memalign_info*)(memptr -
+ sizeof(struct memalign_info));
+
+ free(info->start_addr);
+}
+
+#else
+
+int elf_malloc(void **memptr, size_t alignment, size_t size) {
+ if ((alignment & (alignment - 1)) != 0)
+ return EINVAL;
+
+ if (alignment % sizeof(void*) != 0)
+ alignment = sizeof(void*);
+
+ return posix_memalign(memptr, alignment, size);
+}
+
+void elf_free(void *memptr) {
+ free(memptr);
+}
+
+#endif //HAVE_ELF_POSIX_MEMALIGN
diff --git a/com32/lib/sys/module/elfutils.h b/com32/lib/sys/module/elfutils.h
new file mode 100644
index 00000000..a901ff48
--- /dev/null
+++ b/com32/lib/sys/module/elfutils.h
@@ -0,0 +1,64 @@
+#ifndef ELF_UTILS_H_
+#define ELF_UTILS_H_
+
+#include <elf.h>
+#include <stdlib.h>
+
+/**
+ * elf_get_header - Returns a pointer to the ELF header structure.
+ * @elf_image: pointer to the ELF file image in memory
+ */
+static inline Elf32_Ehdr *elf_get_header(void *elf_image) {
+ return (Elf32_Ehdr*)elf_image;
+}
+
+/**
+ * elf_get_pht - Returns a pointer to the first entry in the PHT.
+ * @elf_image: pointer to the ELF file image in memory
+ */
+static inline Elf32_Phdr *elf_get_pht(void *elf_image) {
+ Elf32_Ehdr *elf_hdr = elf_get_header(elf_image);
+
+ return (Elf32_Phdr*)((Elf32_Off)elf_hdr + elf_hdr->e_phoff);
+}
+
+//
+/**
+ * elf_get_ph - Returns the element with the given index in the PTH
+ * @elf_image: pointer to the ELF file image in memory
+ * @index: the index of the PHT entry to look for
+ */
+static inline Elf32_Phdr *elf_get_ph(void *elf_image, int index) {
+ Elf32_Phdr *elf_pht = elf_get_pht(elf_image);
+ Elf32_Ehdr *elf_hdr = elf_get_header(elf_image);
+
+ return (Elf32_Phdr*)((Elf32_Off)elf_pht + index * elf_hdr->e_phentsize);
+}
+
+/**
+ * elf_hash - Returns the index in a SysV hash table for the symbol name.
+ * @name: the name of the symbol to look for
+ */
+extern unsigned long elf_hash(const unsigned char *name);
+
+/**
+ * elf_gnu_hash - Returns the index in a GNU hash table for the symbol name.
+ * @name: the name of the symbol to look for
+ */
+extern unsigned long elf_gnu_hash(const unsigned char *name);
+
+/**
+ * elf_malloc - Allocates memory to be used by ELF module contents.
+ * @memptr: pointer to a variable to hold the address of the allocated block.
+ * @alignment: alignment constraints of the block
+ * @size: the required size of the block
+ */
+extern int elf_malloc(void **memptr, size_t alignment, size_t size);
+
+/**
+ * elf_free - Releases memory previously allocated by elf_malloc.
+ * @memptr: the address of the allocated block
+ */
+extern void elf_free(char *memptr);
+
+#endif /*ELF_UTILS_H_*/
diff --git a/com32/lib/sys/module/exec.c b/com32/lib/sys/module/exec.c
new file mode 100644
index 00000000..18c8306d
--- /dev/null
+++ b/com32/lib/sys/module/exec.c
@@ -0,0 +1,234 @@
+/*
+ * exec.c
+ *
+ * Created on: Aug 14, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#include <sys/module.h>
+#include <sys/exec.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <setjmp.h>
+#include <alloca.h>
+#include <dprintf.h>
+
+#define DBG_PRINT(fmt, args...) dprintf("[EXEC] " fmt, ##args)
+
+struct elf_module *__syslinux_current = NULL;
+
+int get_module_type(struct elf_module *module)
+{
+ if(module->main_func) return EXEC_MODULE;
+ return LIB_MODULE;
+}
+
+jmp_buf __process_exit_jmp;
+
+#if 0
+int spawnv(const char *name, const char **argv)
+{
+ int res, ret_val = 0;
+ const char **arg;
+ int argc;
+ char **argp, **args;
+ struct elf_module *previous;
+ malloc_tag_t prev_mem_tag;
+
+ struct elf_module *module = module_alloc(name);
+
+ if (module == NULL)
+ return -1;
+
+ res = module_load(module);
+ if (res != 0) {
+ module_unload(module);
+ return res;
+ }
+
+ if (module->main_func == NULL) {
+ // We can't execute without a main function
+ module_unload(module);
+ return -1;
+ }
+ /*if (module->main_func != NULL) {
+ const char **last_arg = argv;
+ void *old_tag;
+ while (*last_arg != NULL)
+ last_arg++;
+
+ // Setup the memory allocation context
+ old_tag = __mem_get_tag_global();
+ __mem_set_tag_global(module);
+
+ // Execute the program
+ ret_val = (*(module->main_func))(last_arg - argv, argv);
+
+ // Clean up the allocation context
+ __free_tagged(module);
+ // Restore the allocation context
+ __mem_set_tag_global(old_tag);
+ } else {
+ // We can't execute without a main function
+ module_unload(module);
+ return -1;
+ }*/
+ // Set up the process context
+ previous = __syslinux_current;
+ prev_mem_tag = __mem_get_tag_global();
+
+ // Setup the new process context
+ __syslinux_current = module;
+ __mem_set_tag_global((malloc_tag_t)module);
+
+ // Generate a new process copy of argv (on the stack)
+ argc = 0;
+ for (arg = argv; *arg; arg++)
+ argc++;
+
+ args = alloca((argc+1) * sizeof(char *));
+
+ for (arg = argv, argp = args; *arg; arg++, argp++) {
+ size_t l = strlen(*arg)+1;
+ *argp = alloca(l);
+ memcpy(*argp, *arg, l);
+ }
+
+ *args = NULL;
+
+ // Execute the program
+ ret_val = setjmp(module->u.x.process_exit);
+
+ if (ret_val)
+ ret_val--; /* Valid range is 0-255 */
+ else if (!module->main_func)
+ ret_val = -1;
+ else
+ exit((module->main_func)(argc, args)); /* Actually run! */
+
+ // Clean up the allocation context
+ __free_tagged(module);
+ // Restore the allocation context
+ __mem_set_tag_global(prev_mem_tag);
+ // Restore the process context
+ __syslinux_current = previous;
+
+ res = module_unload(module);
+
+ if (res != 0) {
+ return res;
+ }
+
+ return ((unsigned int)ret_val & 0xFF);
+}
+
+int spawnl(const char *name, const char *arg, ...)
+{
+ /*
+ * NOTE: We assume the standard ABI specification for the i386
+ * architecture. This code may not work if used in other
+ * circumstances, including non-variadic functions, different
+ * architectures and calling conventions.
+ */
+ return spawnv(name, &arg);
+}
+#endif
+
+/*
+ * Load a module and runs its start function.
+ *
+ * For library modules the start function is module->init_func and for
+ * executable modules its module->main_func.
+ *
+ * "name" is the name of the module to load.
+ *
+ * "argv" and "argc" are only passed to module->main_func, for library
+ * modules these arguments can be NULL and 0, respectively.
+ *
+ * "argv" is an array of arguments to pass to module->main_func.
+ * argv[0] must be a pointer to "name" and argv[argc] must be NULL.
+ *
+ * "argc" is the number of arguments in "argv".
+ */
+int spawn_load(const char *name, int argc, char **argv)
+{
+ int res, ret_val = 0;
+ struct elf_module *previous;
+ //malloc_tag_t prev_mem_tag;
+ struct elf_module *module = module_alloc(name);
+ struct elf_module *cur_module;
+ int type;
+
+ dprintf("enter: name = %s", name);
+
+ if (module == NULL)
+ return -1;
+
+ if (get_module_type(module) == EXEC_MODULE) {
+ if (!argc || !argv || strcmp(argv[0], name)) {
+ res = -1;
+ goto out;
+ }
+ }
+
+ cur_module = module_current();
+ if (!strcmp(cur_module->name, module->name)) {
+ dprintf("We is running this module %s already!", module->name);
+
+ module_unload(cur_module);
+ }
+
+ res = module_load(module);
+ if (res != 0)
+ goto out;
+
+ type = get_module_type(module);
+
+ dprintf("type = %d, prev = %s, cur = %s",
+ type, cur_module->name, module->name);
+
+ if(type==EXEC_MODULE)
+ {
+ previous = __syslinux_current;
+ //prev_mem_tag = __mem_get_tag_global();
+
+ // Setup the new process context
+ __syslinux_current = module;
+ //__mem_set_tag_global((malloc_tag_t)module);
+
+ // Execute the program
+ ret_val = setjmp(module->u.x.process_exit);
+
+ if (ret_val)
+ ret_val--; /* Valid range is 0-255 */
+ else if (!module->main_func)
+ ret_val = -1;
+ else
+ exit((module->main_func)(argc, argv)); /* Actually run! */
+
+ // Clean up the allocation context
+ //__free_tagged(module);
+ // Restore the allocation context
+ //__mem_set_tag_global(prev_mem_tag);
+ // Restore the process context
+ __syslinux_current = previous;
+
+ res = module_unload(module);
+
+ if (res != 0)
+ goto out;
+ }
+
+out:
+ if (res)
+ _module_unload(module);
+ return res;
+}
+
+void exec_term(void)
+{
+ modules_term();
+}
diff --git a/com32/lib/sys/open.c b/com32/lib/sys/open.c
index 3e7bb6cf..1ed5bb4c 100644
--- a/com32/lib/sys/open.c
+++ b/com32/lib/sys/open.c
@@ -30,6 +30,7 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
+#include <fs.h>
#include "file.h"
/*
@@ -56,12 +57,15 @@ int open(const char *pathname, int flags, ...)
struct file_info *fp;
fd = opendev(&__file_dev, NULL, flags);
+
+ //printf("enter, file = %s, fd = %d\n", pathname, fd);
+
if (fd < 0)
return -1;
fp = &__file_info[fd];
- handle = __com32.cs_pm->open_file(pathname, &fp->i.fd);
+ handle = open_file(pathname, flags, &fp->i.fd);
if (handle < 0) {
close(fd);
errno = ENOENT;
diff --git a/com32/lib/sys/rawcon_read.c b/com32/lib/sys/rawcon_read.c
index f7e89c7a..51bb9536 100644
--- a/com32/lib/sys/rawcon_read.c
+++ b/com32/lib/sys/rawcon_read.c
@@ -35,32 +35,38 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <core.h>
#include <minmax.h>
-#include <sys/times.h>
#include "file.h"
/* Global, since it's used by stdcon_read */
ssize_t __rawcon_read(struct file_info *fp, void *buf, size_t count)
{
- com32sys_t ireg, oreg;
char *bufp = buf;
size_t n = 0;
+ static char hi = 0;
+ static bool hi_key = false;
(void)fp;
- memset(&ireg, 0, sizeof ireg);
-
while (n < count) {
+ if (hi_key) {
+ *bufp++ = hi;
+ n++;
+ hi_key = false;
+ continue;
+ }
+
/* Poll */
- ireg.eax.b[1] = 0x0B;
- __intcall(0x21, &ireg, &oreg);
- if (!oreg.eax.b[0])
+ if (!pollchar())
break;
/* We have data, go get it */
- ireg.eax.b[1] = 0x08;
- __intcall(0x21, &ireg, &oreg);
- *bufp++ = oreg.eax.b[0];
+ *bufp = getchar(&hi);
+ if (!*bufp)
+ hi_key = true;
+
+ bufp++;
n++;
}
diff --git a/com32/lib/sys/rawcon_write.c b/com32/lib/sys/rawcon_write.c
index 2d45a7b2..1f7920b2 100644
--- a/com32/lib/sys/rawcon_write.c
+++ b/com32/lib/sys/rawcon_write.c
@@ -34,24 +34,20 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <core.h>
#include <minmax.h>
#include "file.h"
static ssize_t __rawcon_write(struct file_info *fp, const void *buf,
size_t count)
{
- com32sys_t ireg;
const char *bufp = buf;
size_t n = 0;
(void)fp;
- memset(&ireg, 0, sizeof ireg);
- ireg.eax.b[1] = 0x02;
-
while (count--) {
- ireg.edx.b[0] = *bufp++;
- __intcall(0x21, &ireg, NULL);
+ writechr(*bufp++);
n++;
}
diff --git a/com32/lib/sys/readdir.c b/com32/lib/sys/readdir.c
deleted file mode 100644
index d2a8c039..00000000
--- a/com32/lib/sys/readdir.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * readdir.c
- */
-
-#include <dirent.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <com32.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-#include <syslinux/pmapi.h>
-
-DIR *opendir(const char *pathname)
-{
- return __com32.cs_pm->opendir(pathname);
-}
-
-struct dirent *readdir(DIR *dir)
-{
- return __com32.cs_pm->readdir(dir);
-}
-
-int closedir(DIR *dir)
-{
- return __com32.cs_pm->closedir(dir);
-}
diff --git a/com32/lib/sys/screensize.c b/com32/lib/sys/screensize.c
index 340227cd..bcd4496c 100644
--- a/com32/lib/sys/screensize.c
+++ b/com32/lib/sys/screensize.c
@@ -14,7 +14,7 @@ int getscreensize(int fd, int *rows, int *cols)
*rows = fp->o.rows;
*cols = fp->o.cols;
- if (!rows || !cols) {
+ if (!*rows || !*cols) {
errno = ENOTTY;
return -1;
}
diff --git a/com32/lib/sys/serial_write.c b/com32/lib/sys/serial_write.c
index fa0f4f4d..3f949fb7 100644
--- a/com32/lib/sys/serial_write.c
+++ b/com32/lib/sys/serial_write.c
@@ -34,13 +34,13 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <core.h>
#include <minmax.h>
#include <syslinux/config.h>
#include "file.h"
ssize_t __serial_write(struct file_info *fp, const void *buf, size_t count)
{
- com32sys_t ireg;
const char *bufp = buf;
size_t n = 0;
@@ -49,12 +49,8 @@ ssize_t __serial_write(struct file_info *fp, const void *buf, size_t count)
if (!syslinux_serial_console_info()->iobase)
return count; /* Nothing to do */
- memset(&ireg, 0, sizeof ireg);
- ireg.eax.b[1] = 0x04;
-
while (count--) {
- ireg.edx.b[0] = *bufp++;
- __intcall(0x21, &ireg, NULL);
+ write_serial(*bufp++);
n++;
}
diff --git a/com32/lib/sys/stdcon_write.c b/com32/lib/sys/stdcon_write.c
index 9cb2f7db..9bd225f9 100644
--- a/com32/lib/sys/stdcon_write.c
+++ b/com32/lib/sys/stdcon_write.c
@@ -34,6 +34,7 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <core.h>
#include <minmax.h>
#include "file.h"
@@ -57,22 +58,16 @@ static int __stdcon_open(struct file_info *fp)
static ssize_t __stdcon_write(struct file_info *fp, const void *buf,
size_t count)
{
- com32sys_t ireg;
const char *bufp = buf;
size_t n = 0;
(void)fp;
- memset(&ireg, 0, sizeof ireg);
- ireg.eax.b[1] = 0x02;
-
while (count--) {
- if (*bufp == '\n') {
- ireg.edx.b[0] = '\r';
- __intcall(0x21, &ireg, NULL);
- }
- ireg.edx.b[0] = *bufp++;
- __intcall(0x21, &ireg, NULL);
+ if (*bufp == '\n')
+ writechr('\r');
+
+ writechr(*bufp++);
n++;
}
diff --git a/com32/lib/sys/vesa/background.c b/com32/lib/sys/vesa/background.c
index 93577461..15e90895 100644
--- a/com32/lib/sys/vesa/background.c
+++ b/com32/lib/sys/vesa/background.c
@@ -205,7 +205,6 @@ static int read_jpeg_file(FILE * fp, uint8_t * header, int len)
unsigned int bytes_per_row[1];
rv = floadfile(fp, &jpeg_file, &length_of_file, header, len);
- fclose(fp);
if (rv)
goto err;
diff --git a/com32/lib/sys/xserial_write.c b/com32/lib/sys/xserial_write.c
index e399f5fc..8a4fb9e0 100644
--- a/com32/lib/sys/xserial_write.c
+++ b/com32/lib/sys/xserial_write.c
@@ -35,6 +35,7 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <core.h>
#include <minmax.h>
#include <colortbl.h>
#include <syslinux/config.h>
@@ -42,12 +43,7 @@
static void emit(char ch)
{
- static com32sys_t ireg; /* Zeroed with the BSS */
-
- ireg.eax.b[1] = 0x04;
- ireg.edx.b[0] = ch;
-
- __intcall(0x21, &ireg, NULL);
+ write_serial(ch);
}
ssize_t __xserial_write(struct file_info *fp, const void *buf, size_t count)
diff --git a/com32/lib/syslinux/cleanup.c b/com32/lib/syslinux/cleanup.c
index 12140e55..066f174f 100644
--- a/com32/lib/syslinux/cleanup.c
+++ b/com32/lib/syslinux/cleanup.c
@@ -26,15 +26,17 @@
* ----------------------------------------------------------------------- */
#include <syslinux/boot.h>
+#include <syslinux/config.h>
+#include <syslinux/pxe_api.h>
#include <stddef.h>
+#include <bios.h>
#include <com32.h>
+#include <core.h>
void syslinux_final_cleanup(uint16_t flags)
{
- static com32sys_t ireg;
+ if (syslinux_filesystem() == SYSLINUX_FS_PXELINUX)
+ unload_pxe(flags);
- ireg.eax.w[0] = 0x000c;
- ireg.edx.w[0] = flags;
-
- __intcall(0x22, &ireg, NULL);
+ cleanup_hardware();
}
diff --git a/com32/lib/syslinux/config.c b/com32/lib/syslinux/config.c
deleted file mode 100644
index b27aa827..00000000
--- a/com32/lib/syslinux/config.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall
- * be included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * ----------------------------------------------------------------------- */
-
-#include <syslinux/config.h>
-#include <klibc/compiler.h>
-#include <com32.h>
-
-const char *__syslinux_config_file;
-
-void __constructor __syslinux_get_config_file_name(void)
-{
- static com32sys_t reg;
-
- reg.eax.w[0] = 0x000e;
- __intcall(0x22, &reg, &reg);
- __syslinux_config_file = MK_PTR(reg.es, reg.ebx.w[0]);
-}
diff --git a/com32/lib/syslinux/debug.c b/com32/lib/syslinux/debug.c
new file mode 100644
index 00000000..d9ab863f
--- /dev/null
+++ b/com32/lib/syslinux/debug.c
@@ -0,0 +1,95 @@
+#include <linux/list.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef DYNAMIC_DEBUG
+
+static LIST_HEAD(debug_funcs);
+
+struct debug_func_entry {
+ const char *name;
+ struct list_head list;
+};
+
+static struct debug_func_entry *lookup_entry(const char *func)
+{
+ struct debug_func_entry *e, *entry = NULL;
+
+ list_for_each_entry(e, &debug_funcs, list) {
+ if (!strcmp(e->name, func)) {
+ entry = e;
+ break;
+ }
+ }
+
+ return entry;
+}
+
+bool __syslinux_debug_enabled(const char *func)
+{
+ struct debug_func_entry *entry;
+
+ entry = lookup_entry(func);
+ if (entry)
+ return true;
+
+ return false;
+}
+
+static int __enable(const char *func)
+{
+ struct debug_func_entry *entry;
+
+ entry = lookup_entry(func);
+ if (entry)
+ return 0; /* already enabled */
+
+ entry = malloc(sizeof(*entry));
+ if (!entry)
+ return -1;
+
+ entry->name = func;
+ list_add(&entry->list, &debug_funcs);
+ return 0;
+}
+
+static int __disable(const char *func)
+{
+ struct debug_func_entry *entry;
+
+ entry = lookup_entry(func);
+ if (!entry)
+ return 0; /* already disabled */
+
+ list_del(&entry->list);
+ free(entry);
+ return 0;
+}
+
+/*
+ * Enable or disable debug code for function 'func'.
+ */
+int syslinux_debug(const char *func, bool enable)
+{
+ int rv;
+
+ if (enable)
+ rv = __enable(func);
+ else
+ rv = __disable(func);
+
+ return rv;
+}
+
+#else
+
+int syslinux_debug(const char *func, bool enable)
+{
+ (void)func;
+ (void)enable;
+
+ printf("Dynamic debug unavailable\n");
+ return -1;
+}
+
+#endif /* DYNAMIC_DEBUG */
diff --git a/com32/lib/syslinux/disk.c b/com32/lib/syslinux/disk.c
index d6409af6..0b0c737e 100644
--- a/com32/lib/syslinux/disk.c
+++ b/com32/lib/syslinux/disk.c
@@ -33,6 +33,7 @@
* Deal with disks and partitions
*/
+#include <core.h>
#include <dprintf.h>
#include <stdio.h>
#include <stdlib.h>
@@ -73,7 +74,8 @@ int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
int disk_get_params(int disk, struct disk_info *const diskinfo)
{
static com32sys_t inreg, outreg;
- struct disk_ebios_eparam *eparam = __com32.cs_bounce;
+ struct disk_ebios_eparam *eparam;
+ int rv = 0;
memset(diskinfo, 0, sizeof *diskinfo);
diskinfo->disk = disk;
@@ -93,6 +95,10 @@ int disk_get_params(int disk, struct disk_info *const diskinfo)
diskinfo->ebios = 1;
}
+ eparam = lmalloc(sizeof *eparam);
+ if (!eparam)
+ return -1;
+
/* Get extended disk parameters if ebios == 1 */
if (diskinfo->ebios) {
memset(&inreg, 0, sizeof inreg);
@@ -127,8 +133,10 @@ int disk_get_params(int disk, struct disk_info *const diskinfo)
__intcall(0x13, &inreg, &outreg);
- if (outreg.eflags.l & EFLAGS_CF)
- return diskinfo->ebios ? 0 : -1;
+ if (outreg.eflags.l & EFLAGS_CF) {
+ rv = diskinfo->ebios ? 0 : -1;
+ goto out;
+ }
diskinfo->spt = 0x3f & outreg.ecx.b[0];
diskinfo->head = 1 + outreg.edx.b[1];
@@ -145,7 +153,86 @@ int disk_get_params(int disk, struct disk_info *const diskinfo)
if (!diskinfo->lbacnt)
diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt;
- return 0;
+out:
+ lfree(eparam);
+ return rv;
+}
+
+/**
+ * Fill inreg based on EBIOS addressing properties.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v inreg Register data structure to be filled.
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @v op_code Code to write/read operation
+ * @ret lmalloc'd buf upon success, NULL upon failure
+ */
+static void *ebios_setup(const struct disk_info *const diskinfo, com32sys_t *inreg,
+ uint64_t lba, uint8_t count, uint8_t op_code)
+{
+ static __lowmem struct disk_ebios_dapa dapa;
+ void *buf;
+
+ buf = lmalloc(count * diskinfo->bps);
+ if (!buf)
+ return NULL;
+
+ dapa.len = sizeof(dapa);
+ dapa.count = count;
+ dapa.off = OFFS(buf);
+ dapa.seg = SEG(buf);
+ dapa.lba = lba;
+
+ inreg->eax.b[1] = op_code;
+ inreg->esi.w[0] = OFFS(&dapa);
+ inreg->ds = SEG(&dapa);
+ inreg->edx.b[0] = diskinfo->disk;
+
+ return buf;
+}
+
+/**
+ * Fill inreg based on CHS addressing properties.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v inreg Register data structure to be filled.
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @v op_code Code to write/read operation
+ * @ret lmalloc'd buf upon success, NULL upon failure
+ */
+static void *chs_setup(const struct disk_info *const diskinfo, com32sys_t *inreg,
+ uint64_t lba, uint8_t count, uint8_t op_code)
+{
+ unsigned int c, h, s, t;
+ void *buf;
+
+ buf = lmalloc(count * diskinfo->bps);
+ if (!buf)
+ return NULL;
+
+ /*
+ * if we passed lba + count check and we get here, that means that
+ * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
+ * 32bits are perfectly enough and lbacnt corresponds to cylinder
+ * boundary
+ */
+ s = lba % diskinfo->spt;
+ t = lba / diskinfo->spt;
+ h = t % diskinfo->head;
+ c = t / diskinfo->head;
+
+ inreg->eax.b[0] = count;
+ inreg->eax.b[1] = op_code;
+ inreg->ecx.b[1] = c;
+ inreg->ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
+ inreg->edx.b[1] = h;
+ inreg->edx.b[0] = diskinfo->disk;
+ inreg->ebx.w[0] = OFFS(buf);
+ inreg->es = SEG(buf);
+
+ return buf;
}
/**
@@ -163,57 +250,33 @@ void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba,
uint8_t count)
{
com32sys_t inreg;
- struct disk_ebios_dapa *dapa = __com32.cs_bounce;
- void *buf = (char *)__com32.cs_bounce + diskinfo->bps;
- void *data;
+ void *buf;
+ void *data = NULL;
uint32_t maxcnt;
+ uint32_t size = 65536;
- maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps;
+ maxcnt = (size - diskinfo->bps) / diskinfo->bps;
if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
return NULL;
memset(&inreg, 0, sizeof inreg);
- if (diskinfo->ebios) {
- dapa->len = sizeof(*dapa);
- dapa->count = count;
- dapa->off = OFFS(buf);
- dapa->seg = SEG(buf);
- dapa->lba = lba;
-
- inreg.esi.w[0] = OFFS(dapa);
- inreg.ds = SEG(dapa);
- inreg.edx.b[0] = diskinfo->disk;
- inreg.eax.b[1] = 0x42; /* Extended read */
- } else {
- unsigned int c, h, s, t;
- /*
- * if we passed lba + count check and we get here, that means that
- * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
- * 32bits are perfectly enough and lbacnt corresponds to cylinder
- * boundary
- */
- s = lba % diskinfo->spt;
- t = lba / diskinfo->spt;
- h = t % diskinfo->head;
- c = t / diskinfo->head;
-
- inreg.eax.b[0] = count;
- inreg.eax.b[1] = 0x02; /* Read */
- inreg.ecx.b[1] = c;
- inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
- inreg.edx.b[1] = h;
- inreg.edx.b[0] = diskinfo->disk;
- inreg.ebx.w[0] = OFFS(buf);
- inreg.es = SEG(buf);
- }
+ if (diskinfo->ebios)
+ buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_READ_CODE);
+ else
+ buf = chs_setup(diskinfo, &inreg, lba, count, CHS_READ_CODE);
- if (disk_int13_retry(&inreg, NULL))
+ if (!buf)
return NULL;
+ if (disk_int13_retry(&inreg, NULL))
+ goto out;
+
data = malloc(count * diskinfo->bps);
if (data)
memcpy(data, buf, count * diskinfo->bps);
+out:
+ lfree(buf);
return data;
}
@@ -233,55 +296,34 @@ int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba,
const void *data, uint8_t count)
{
com32sys_t inreg;
- struct disk_ebios_dapa *dapa = __com32.cs_bounce;
- void *buf = (char *)__com32.cs_bounce + diskinfo->bps;
+ void *buf;
uint32_t maxcnt;
+ uint32_t size = 65536;
+ int rv = -1;
- maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps;
+ maxcnt = (size - diskinfo->bps) / diskinfo->bps;
if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
return -1;
- memcpy(buf, data, count * diskinfo->bps);
memset(&inreg, 0, sizeof inreg);
- if (diskinfo->ebios) {
- dapa->len = sizeof(*dapa);
- dapa->count = count;
- dapa->off = OFFS(buf);
- dapa->seg = SEG(buf);
- dapa->lba = lba;
-
- inreg.esi.w[0] = OFFS(dapa);
- inreg.ds = SEG(dapa);
- inreg.edx.b[0] = diskinfo->disk;
- inreg.eax.b[1] = 0x43; /* Extended write */
- } else {
- unsigned int c, h, s, t;
- /*
- * if we passed lba + count check and we get here, that means that
- * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
- * 32bits are perfectly enough and lbacnt corresponds to cylinder
- * boundary
- */
- s = lba % diskinfo->spt;
- t = lba / diskinfo->spt;
- h = t % diskinfo->head;
- c = t / diskinfo->head;
-
- inreg.eax.b[0] = count;
- inreg.eax.b[1] = 0x03; /* Write */
- inreg.ecx.b[1] = c;
- inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
- inreg.edx.b[1] = h;
- inreg.edx.b[0] = diskinfo->disk;
- inreg.ebx.w[0] = OFFS(buf);
- inreg.es = SEG(buf);
- }
+ if (diskinfo->ebios)
+ buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_WRITE_CODE);
+ else
+ buf = chs_setup(diskinfo, &inreg, lba, count, CHS_WRITE_CODE);
- if (disk_int13_retry(&inreg, NULL))
+ if (!buf)
return -1;
- return 0; /* ok */
+ memcpy(buf, data, count * diskinfo->bps);
+
+ if (disk_int13_retry(&inreg, NULL))
+ goto out;
+
+ rv = 0; /* ok */
+out:
+ lfree(buf);
+ return rv;
}
/**
diff --git a/com32/lib/syslinux/idle.c b/com32/lib/syslinux/idle.c
index ddaa7fcd..33e8035c 100644
--- a/com32/lib/syslinux/idle.c
+++ b/com32/lib/syslinux/idle.c
@@ -33,15 +33,16 @@
#include <stddef.h>
#include <com32.h>
+#include <core.h>
#include <syslinux/pmapi.h>
#include <syslinux/idle.h>
void syslinux_reset_idle(void)
{
- __com32.cs_pm->reset_idle();
+ reset_idle();
}
void syslinux_idle(void)
{
- __com32.cs_pm->idle();
+ __idle();
}
diff --git a/com32/lib/syslinux/initramfs_file.c b/com32/lib/syslinux/initramfs_file.c
index 763eff28..7eb55b5e 100644
--- a/com32/lib/syslinux/initramfs_file.c
+++ b/com32/lib/syslinux/initramfs_file.c
@@ -65,7 +65,7 @@ static size_t initramfs_mkdirs(const char *filename, void *buffer,
const char *p = filename;
char *bp = buffer;
int len;
- size_t bytes = 0;
+ size_t bytes = 0, hdr_sz;
int pad;
while ((p = strchr(p, '/'))) {
@@ -81,15 +81,17 @@ static size_t initramfs_mkdirs(const char *filename, void *buffer,
while ((p = strchr(p, '/'))) {
if (p != filename && p[-1] != '/') {
len = p - filename;
+ hdr_sz = ((sizeof(struct cpio_header) + len + 1) + 3) & ~3;
bp += sprintf(bp, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x"
"%08x%08x%08x%08x", next_ino++, S_IFDIR | 0755,
0, 0, 1, 0, 0, 0, 1, 0, 1, len + 1, 0);
memcpy(bp, filename, len);
bp += len;
- pad = (-(sizeof(struct cpio_header) + len) & 3) + 1;
+ pad = hdr_sz - (sizeof(struct cpio_header) + len);
memset(bp, 0, pad);
bp += pad;
}
+ p++;
}
}
@@ -104,7 +106,7 @@ int initramfs_mknod(struct initramfs *ihead, const char *filename,
int do_mkdir,
uint16_t mode, size_t len, uint32_t major, uint32_t minor)
{
- size_t bytes;
+ size_t bytes, hdr_sz;
int namelen = strlen(filename);
int pad;
char *buffer, *bp;
@@ -114,7 +116,8 @@ int initramfs_mknod(struct initramfs *ihead, const char *filename,
else
bytes = 0;
- bytes += ((sizeof(struct cpio_header) + namelen + 1) + 3) & ~3;
+ hdr_sz = ((sizeof(struct cpio_header) + namelen + 1) + 3) & ~3;
+ bytes += hdr_sz;
bp = buffer = malloc(bytes);
if (!buffer)
@@ -127,8 +130,8 @@ int initramfs_mknod(struct initramfs *ihead, const char *filename,
"%08x%08x%08x%08x", next_ino++, mode,
0, 0, 1, 0, len, 0, 1, major, minor, namelen + 1, 0);
memcpy(bp, filename, namelen);
- bp += len;
- pad = (-(sizeof(struct cpio_header) + namelen) & 3) + 1;
+ bp += namelen;
+ pad = hdr_sz - (sizeof(struct cpio_header) + namelen);
memset(bp, 0, pad);
if (initramfs_add_data(ihead, buffer, bytes, bytes, 4)) {
diff --git a/com32/lib/syslinux/ipappend.c b/com32/lib/syslinux/ipappend.c
index bd000920..11eb1bf5 100644
--- a/com32/lib/syslinux/ipappend.c
+++ b/com32/lib/syslinux/ipappend.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -31,28 +32,16 @@
* Get ipappend strings
*/
+#include <syslinux/sysappend.h>
#include <syslinux/config.h>
+#include <syslinux/pmapi.h>
#include <klibc/compiler.h>
-#include <com32.h>
+#include <core.h>
struct syslinux_ipappend_strings __syslinux_ipappend_strings;
-static const char *syslinux_ipappend_string_list[32];
void __constructor __syslinux_get_ipappend_strings(void)
{
- static com32sys_t reg;
- int i;
-
- reg.eax.w[0] = 0x000f;
- __intcall(0x22, &reg, &reg);
-
- if (!(reg.eflags.l & EFLAGS_CF)) {
- __syslinux_ipappend_strings.count = reg.ecx.w[0];
- __syslinux_ipappend_strings.ptr = syslinux_ipappend_string_list;
- for (i = 0; i < reg.ecx.w[0]; i++) {
- syslinux_ipappend_string_list[i] =
- MK_PTR(reg.es,
- *(uint16_t *) MK_PTR(reg.es, reg.ebx.w[0] + i * 2));
- }
- }
+ __syslinux_ipappend_strings.count = SYSAPPEND_MAX,
+ __syslinux_ipappend_strings.ptr = sysappend_strings;
}
diff --git a/com32/lib/syslinux/keyboard.c b/com32/lib/syslinux/keyboard.c
index feafde0d..03bd216a 100644
--- a/com32/lib/syslinux/keyboard.c
+++ b/com32/lib/syslinux/keyboard.c
@@ -26,19 +26,13 @@
* ----------------------------------------------------------------------- */
#include <syslinux/keyboard.h>
-#include <com32.h>
+#include <core.h>
struct syslinux_keyboard_map __syslinux_keyboard_map;
void __constructor __syslinux_get_keyboard_map(void)
{
- static com32sys_t reg;
-
- reg.eax.w[0] = 0x001e;
- __intcall(0x22, &reg, &reg);
- if (!(reg.eflags.l & EFLAGS_CF)) {
- __syslinux_keyboard_map.version = reg.eax.w[0];
- __syslinux_keyboard_map.length = reg.ecx.w[0];
- __syslinux_keyboard_map.map = MK_PTR(reg.es, reg.ebx.w[0]);
- }
+ __syslinux_keyboard_map.version = 1;
+ __syslinux_keyboard_map.length = sizeof(KbdMap);
+ __syslinux_keyboard_map.map = (void *)KbdMap;
}
diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c
index 856141f8..7638e6f6 100644
--- a/com32/lib/syslinux/load_linux.c
+++ b/com32/lib/syslinux/load_linux.c
@@ -1,7 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
- * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2009-2013 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -40,11 +40,13 @@
#include <minmax.h>
#include <errno.h>
#include <suffix_number.h>
+#include <graphics.h>
+#include <dprintf.h>
+
#include <syslinux/align.h>
#include <syslinux/linux.h>
#include <syslinux/bootrm.h>
#include <syslinux/movebits.h>
-#include <dprintf.h>
struct linux_header {
uint8_t boot_sector_1[0x0020];
@@ -202,8 +204,11 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
cmdline_size = strlen(cmdline) + 1;
- if (kernel_size < 2 * 512)
+ errno = EINVAL;
+ if (kernel_size < 2 * 512) {
+ dprintf("Kernel size too small\n");
goto bail;
+ }
/* Look for specific command-line arguments we care about */
if ((arg = find_argument(cmdline, "mem=")))
@@ -234,8 +239,10 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
memcpy(&hdr, kernel_buf, sizeof hdr);
whdr = (struct linux_header *)kernel_buf;
- if (hdr.boot_flag != BOOT_MAGIC)
+ if (hdr.boot_flag != BOOT_MAGIC) {
+ dprintf("Invalid boot magic\n");
goto bail;
+ }
if (hdr.header != LINUX_MAGIC) {
hdr.version = 0x0100; /* Very old kernel */
@@ -247,7 +254,7 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
if (!hdr.setup_sects)
hdr.setup_sects = 4;
- if (hdr.version < 0x0203)
+ if (hdr.version < 0x0203 || !hdr.initrd_addr_max)
hdr.initrd_addr_max = 0x37ffffff;
if (!memlimit && memlimit - 1 > hdr.initrd_addr_max)
@@ -285,11 +292,18 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
hdr.init_size = 3 * prot_mode_size;
}
- if (!(hdr.loadflags & LOAD_HIGH) && prot_mode_size > 512 * 1024)
- goto bail; /* Kernel cannot be loaded low */
+ if (!(hdr.loadflags & LOAD_HIGH) && prot_mode_size > 512 * 1024) {
+ dprintf("Kernel cannot be loaded low\n");
+ goto bail;
+ }
+
+ /* Get the size of the initramfs, if there is one */
+ irf_size = initramfs_size(initramfs);
- if (initramfs && hdr.version < 0x0200)
- goto bail; /* initrd/initramfs not supported */
+ if (irf_size && hdr.version < 0x0200) {
+ dprintf("Initrd specified but not supported by kernel\n");
+ goto bail;
+ }
if (hdr.version >= 0x0200) {
whdr->type_of_loader = 0x30; /* SYSLINUX unknown module */
@@ -297,21 +311,15 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
whdr->heap_end_ptr = cmdline_offset - 0x0200;
whdr->loadflags |= CAN_USE_HEAP;
}
- if (hdr.version >= 0x0202) {
- whdr->cmd_line_ptr = real_mode_base + cmdline_offset;
- } else {
- whdr->old_cmd_line_magic = OLD_CMDLINE_MAGIC;
- whdr->old_cmd_line_offset = cmdline_offset;
- /* Be paranoid and round up to a multiple of 16 */
- whdr->setup_move_size = (cmdline_offset + cmdline_size + 15) & ~15;
- }
}
/* Get the memory map */
mmap = syslinux_memory_map(); /* Memory map for shuffle_boot */
amap = syslinux_dup_memmap(mmap); /* Keep track of available memory */
- if (!mmap || !amap)
+ if (!mmap || !amap) {
+ errno = ENOMEM;
goto bail;
+ }
dprintf("Initial memory map:\n");
syslinux_dump_memmap(mmap);
@@ -321,17 +329,22 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
it's unavailable to the boot loader, which probably has already touched
some of it), or just in the amap? */
if (memlimit)
- if (syslinux_add_memmap(&amap, memlimit, -memlimit, SMT_RESERVED))
+ if (syslinux_add_memmap(&amap, memlimit, -memlimit, SMT_RESERVED)) {
+ errno = ENOMEM;
goto bail;
+ }
/* Place the kernel in memory */
/* First, find a suitable place for the protected-mode code */
- if (syslinux_memmap_type(amap, prot_mode_base, prot_mode_size)
+ if (prot_mode_size &&
+ syslinux_memmap_type(amap, prot_mode_base, prot_mode_size)
!= SMT_FREE) {
const struct syslinux_memmap *mp;
- if (!hdr.relocatable_kernel)
- goto bail; /* Can't relocate - no hope */
+ if (!hdr.relocatable_kernel) {
+ dprintf("Cannot relocate kernel\n");
+ goto bail;
+ }
ok = false;
for (mp = amap; mp; mp = mp->next) {
@@ -360,8 +373,10 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
}
}
- if (!ok)
+ if (!ok) {
+ dprintf("Could not find location for protected-mode code\n");
goto bail;
+ }
}
/* Real mode code */
@@ -393,39 +408,65 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
break;
}
}
+
+ if (!ok) {
+ dprintf("Could not find location for real-mode code\n");
+ goto bail;
+ }
}
if (syslinux_add_movelist(&fraglist, real_mode_base, (addr_t) kernel_buf,
real_mode_size))
goto bail;
if (syslinux_add_memmap
- (&amap, real_mode_base, cmdline_offset + cmdline_size, SMT_ALLOC))
+ (&amap, real_mode_base, cmdline_offset + cmdline_size, SMT_ALLOC)) {
+ errno = ENOMEM;
goto bail;
+ }
/* Zero region between real mode code and cmdline */
if (syslinux_add_memmap(&mmap, real_mode_base + real_mode_size,
- cmdline_offset - real_mode_size, SMT_ZERO))
+ cmdline_offset - real_mode_size, SMT_ZERO)) {
+ errno = ENOMEM;
goto bail;
+ }
/* Command line */
if (syslinux_add_movelist(&fraglist, real_mode_base + cmdline_offset,
- (addr_t) cmdline, cmdline_size))
+ (addr_t) cmdline, cmdline_size)) {
+ errno = ENOMEM;
goto bail;
+ }
+ if (hdr.version >= 0x0202) {
+ whdr->cmd_line_ptr = real_mode_base + cmdline_offset;
+ } else {
+ whdr->old_cmd_line_magic = OLD_CMDLINE_MAGIC;
+ whdr->old_cmd_line_offset = cmdline_offset;
+ if (hdr.version >= 0x0200) {
+ /* Be paranoid and round up to a multiple of 16 */
+ whdr->setup_move_size = (cmdline_offset + cmdline_size + 15) & ~15;
+ }
+ }
/* Protected-mode code */
- if (syslinux_add_movelist(&fraglist, prot_mode_base,
- (addr_t) kernel_buf + real_mode_size,
- prot_mode_size))
- goto bail;
- if (syslinux_add_memmap(&amap, prot_mode_base, prot_mode_size, SMT_ALLOC))
- goto bail;
+ if (prot_mode_size) {
+ if (syslinux_add_movelist(&fraglist, prot_mode_base,
+ (addr_t) kernel_buf + real_mode_size,
+ prot_mode_size)) {
+ errno = ENOMEM;
+ goto bail;
+ }
+ if (syslinux_add_memmap(&amap, prot_mode_base, prot_mode_size,
+ SMT_ALLOC)) {
+ errno = ENOMEM;
+ goto bail;
+ }
+ }
/* Figure out the size of the initramfs, and where to put it.
We should put it at the highest possible address which is
<= hdr.initrd_addr_max, which fits the entire initramfs. */
- irf_size = initramfs_size(initramfs); /* Handles initramfs == NULL */
-
if (irf_size) {
addr_t best_addr = 0;
struct syslinux_memmap *ml;
@@ -439,17 +480,23 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
best_addr = (adj_end - irf_size) & ~align_mask;
}
- if (!best_addr)
- goto bail; /* Insufficient memory for initramfs */
+ if (!best_addr) {
+ dprintf("Insufficient memory for initramfs\n");
+ goto bail;
+ }
whdr->ramdisk_image = best_addr;
whdr->ramdisk_size = irf_size;
- if (syslinux_add_memmap(&amap, best_addr, irf_size, SMT_ALLOC))
+ if (syslinux_add_memmap(&amap, best_addr, irf_size, SMT_ALLOC)) {
+ errno = ENOMEM;
goto bail;
+ }
- if (map_initramfs(&fraglist, &mmap, initramfs, best_addr))
+ if (map_initramfs(&fraglist, &mmap, initramfs, best_addr)) {
+ errno = ENOMEM;
goto bail;
+ }
}
}
@@ -485,14 +532,20 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
*prev_ptr = best_addr;
prev_ptr = &sdp->hdr.next;
- if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC))
+ if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC)) {
+ errno = ENOMEM;
goto bail;
+ }
if (syslinux_add_movelist(&fraglist, best_addr,
- (addr_t)&sdp->hdr, sizeof sdp->hdr))
+ (addr_t)&sdp->hdr, sizeof sdp->hdr)) {
+ errno = ENOMEM;
goto bail;
+ }
if (syslinux_add_movelist(&fraglist, best_addr + sizeof sdp->hdr,
- (addr_t)sdp->data, sdp->hdr.len))
+ (addr_t)sdp->data, sdp->hdr.len)) {
+ errno = ENOMEM;
goto bail;
+ }
}
}
@@ -513,7 +566,19 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
dprintf("Initial movelist:\n");
syslinux_dump_movelist(fraglist);
+ if (video_mode != 0x0f04) {
+ /*
+ * video_mode is not "current", so if we are in graphics mode we
+ * need to revert to text mode...
+ */
+ dprintf("*** Calling syslinux_force_text_mode()...\n");
+ syslinux_force_text_mode();
+ } else {
+ dprintf("*** vga=current, not calling syslinux_force_text_mode()...\n");
+ }
+
syslinux_shuffle_boot_rm(fraglist, mmap, 0, &regs);
+ dprintf("shuffle_boot_rm failed\n");
bail:
syslinux_free_movelist(fraglist);
diff --git a/com32/lib/syslinux/localboot.c b/com32/lib/syslinux/localboot.c
index 3b480c71..16016a9d 100644
--- a/com32/lib/syslinux/localboot.c
+++ b/com32/lib/syslinux/localboot.c
@@ -28,15 +28,11 @@
#include <syslinux/boot.h>
#include <stddef.h>
#include <com32.h>
+#include <localboot.h>
/* This returns only on failure */
-void syslinux_local_boot(uint16_t flags)
+void syslinux_local_boot(int16_t flags)
{
- static com32sys_t ireg;
-
- ireg.eax.w[0] = 0x0014;
- ireg.edx.w[0] = flags;
-
- __intcall(0x22, &ireg, NULL);
+ local_boot(flags);
}
diff --git a/com32/lib/syslinux/pxe_dns.c b/com32/lib/syslinux/pxe_dns.c
index 6620396f..b813b543 100644
--- a/com32/lib/syslinux/pxe_dns.c
+++ b/com32/lib/syslinux/pxe_dns.c
@@ -43,12 +43,12 @@
or -1 on invocation failure */
uint32_t pxe_dns(const char *hostname)
{
- com32sys_t regs;
union {
unsigned char b[4];
uint32_t ip;
} q;
char *lm_hostname;
+ uint32_t status;
/* Is this a dot-quad? */
if (sscanf(hostname, "%hhu.%hhu.%hhu.%hhu",
@@ -59,17 +59,9 @@ uint32_t pxe_dns(const char *hostname)
if (!lm_hostname)
return 0;
- memset(&regs, 0, sizeof regs);
- regs.eax.w[0] = 0x0010;
- regs.es = SEG(lm_hostname);
- /* regs.ebx.w[0] = OFFS(lm_hostname); */
-
- __intcall(0x22, &regs, &regs);
+ status = dns_resolv(lm_hostname);
lfree(lm_hostname);
- if (regs.eflags.l & EFLAGS_CF)
- return 0;
-
- return regs.eax.l;
+ return status;
}
diff --git a/com32/lib/syslinux/pxe_get_cached.c b/com32/lib/syslinux/pxe_get_cached.c
index 47040378..a090a4c9 100644
--- a/com32/lib/syslinux/pxe_get_cached.c
+++ b/com32/lib/syslinux/pxe_get_cached.c
@@ -43,7 +43,6 @@
int pxe_get_cached_info(int level, void **buf, size_t * len)
{
const int max_dhcp_packet = 2048;
- com32sys_t regs;
t_PXENV_GET_CACHED_INFO *gci;
void *bbuf, *nbuf;
int err;
@@ -52,12 +51,6 @@ int pxe_get_cached_info(int level, void **buf, size_t * len)
if (!gci)
return -1;
- memset(&regs, 0, sizeof regs);
- regs.eax.w[0] = 0x0009;
- regs.ebx.w[0] = PXENV_GET_CACHED_INFO;
- regs.es = SEG(gci);
- /* regs.edi.w[0] = OFFS(gci); */
-
bbuf = &gci[1];
gci->Status = PXENV_STATUS_FAILURE;
@@ -66,9 +59,9 @@ int pxe_get_cached_info(int level, void **buf, size_t * len)
gci->Buffer.seg = SEG(bbuf);
gci->Buffer.offs = OFFS(bbuf);
- __intcall(0x22, &regs, &regs);
+ err = pxe_call(PXENV_GET_CACHED_INFO, gci);
- if (regs.eflags.l & EFLAGS_CF) {
+ if (err) {
err = -1;
goto exit;
}
diff --git a/com32/lib/syslinux/pxe_get_nic.c b/com32/lib/syslinux/pxe_get_nic.c
index b301a75a..6e256f92 100644
--- a/com32/lib/syslinux/pxe_get_nic.c
+++ b/com32/lib/syslinux/pxe_get_nic.c
@@ -42,25 +42,19 @@
or -1 on invocation failure */
int pxe_get_nic_type(t_PXENV_UNDI_GET_NIC_TYPE *gnt)
{
- com32sys_t regs;
t_PXENV_UNDI_GET_NIC_TYPE *lgnt;
+ int err;
lgnt = lzalloc(sizeof *lgnt);
if (!lgnt)
return -1;
- memset(&regs, 0, sizeof regs);
- regs.eax.w[0] = 0x0009;
- regs.ebx.w[0] = PXENV_UNDI_GET_NIC_TYPE;
- regs.es = SEG(lgnt);
- /* regs.edi.w[0] = OFFS(lgnt); */
-
- __intcall(0x22, &regs, &regs);
+ err = pxe_call(PXENV_UNDI_GET_NIC_TYPE, lgnt);
memcpy(gnt, lgnt, sizeof(t_PXENV_UNDI_GET_NIC_TYPE));
lfree(lgnt);
- if (regs.eflags.l & EFLAGS_CF)
+ if (err)
return -1;
return gnt->Status;
diff --git a/com32/lib/syslinux/run_command.c b/com32/lib/syslinux/run_command.c
index a0ac9a0d..7e4dc41b 100644
--- a/com32/lib/syslinux/run_command.c
+++ b/com32/lib/syslinux/run_command.c
@@ -28,21 +28,16 @@
#include <syslinux/boot.h>
#include <stddef.h>
#include <string.h>
-#include <com32.h>
+#include <core.h>
int syslinux_run_command(const char *command)
{
- static com32sys_t ireg;
char *lm_command = lstrdup(command);
if (!lm_command)
return -1;
- ireg.eax.w[0] = 0x0003;
- ireg.es = SEG(lm_command);
- /* ireg.ebx.w[0] = OFFS(lm_command); */
-
- __intcall(0x22, &ireg, NULL);
+ load_kernel(lm_command);
/* Should not return even on failure, but in case... */
lfree(lm_command);
diff --git a/com32/lib/syslinux/run_default.c b/com32/lib/syslinux/run_default.c
index 8dc9fbe4..0cfa5470 100644
--- a/com32/lib/syslinux/run_default.c
+++ b/com32/lib/syslinux/run_default.c
@@ -26,16 +26,14 @@
* ----------------------------------------------------------------------- */
#include <syslinux/boot.h>
+#include <core.h>
#include <stddef.h>
-#include <com32.h>
+
+extern const char *default_cmd;
__noreturn syslinux_run_default(void)
{
- static com32sys_t ireg;
-
- ireg.eax.w[0] = 0x0004;
- __intcall(0x22, &ireg, NULL);
-
+ load_kernel(default_cmd);
/* Should not return even on failure */
for (;;) ;
}
diff --git a/com32/lib/syslinux/runimage.c b/com32/lib/syslinux/runimage.c
index d5cdbc62..4dcd029d 100644
--- a/com32/lib/syslinux/runimage.c
+++ b/com32/lib/syslinux/runimage.c
@@ -34,37 +34,26 @@
#include <stdlib.h>
#include <string.h>
#include <syslinux/boot.h>
-#include <com32.h>
+#include <syslinux/config.h>
+#include <core.h>
void syslinux_run_kernel_image(const char *filename, const char *cmdline,
uint32_t ipappend_flags, uint32_t type)
{
- static com32sys_t ireg;
- char *bbfilename = NULL;
char *bbcmdline = NULL;
+ size_t len;
+ int rv;
- bbfilename = lstrdup(filename);
- if (!bbfilename)
- goto fail;
-
- bbcmdline = lstrdup(cmdline);
+ /* +2 for NULL and space */
+ len = strlen(filename) + strlen(cmdline) + 2;
+ bbcmdline = malloc(len);
if (!bbcmdline)
- goto fail;
-
-
- ireg.eax.w[0] = 0x0016;
- ireg.ds = SEG(bbfilename);
- /* ireg.esi.w[0] = OFFS(bbfilename); */
- ireg.es = SEG(bbcmdline);
- /* ireg.ebx.w[0] = OFFS(bbcmdline); */
- ireg.ecx.l = ipappend_flags;
- ireg.edx.l = type;
+ return;
- __intcall(0x22, &ireg, 0);
+ rv = snprintf(bbcmdline, len, "%s %s", filename, cmdline);
+ if (rv == -1 || (size_t)rv >= len)
+ return;
-fail:
- if (bbcmdline)
- lfree(bbcmdline);
- if (bbfilename)
- lfree(bbfilename);
+ SysAppends = ipappend_flags;
+ execute(bbcmdline, type, true);
}
diff --git a/com32/lib/syslinux/serial.c b/com32/lib/syslinux/serial.c
index f06e8c8e..aa5690fa 100644
--- a/com32/lib/syslinux/serial.c
+++ b/com32/lib/syslinux/serial.c
@@ -34,19 +34,22 @@
#include <klibc/compiler.h>
#include <syslinux/config.h>
#include <string.h>
-#include <com32.h>
+#include <bios.h>
+#include <core.h>
struct syslinux_serial_console_info __syslinux_serial_console_info;
-void __constructor __syslinux_get_serial_console_info(void)
+void __syslinux_set_serial_console_info(void)
{
- static com32sys_t reg;
+ uint16_t flowctl;
- memset(&reg, 0, sizeof reg);
- reg.eax.w[0] = 0x000b;
- __intcall(0x22, &reg, &reg);
+ __syslinux_serial_console_info.iobase = SerialPort;
+ __syslinux_serial_console_info.divisor = BaudDivisor;
- __syslinux_serial_console_info.iobase = reg.edx.w[0];
- __syslinux_serial_console_info.divisor = reg.ecx.w[0];
- __syslinux_serial_console_info.flowctl = reg.ebx.w[0];
+ flowctl = FlowOutput | FlowInput | (FlowIgnore << 4);
+
+ if (!DisplayCon)
+ flowctl |= (0x80 << 8);
+
+ __syslinux_serial_console_info.flowctl = flowctl;
}
diff --git a/com32/lib/syslinux/shuffle.c b/com32/lib/syslinux/shuffle.c
index e9ee6aad..1dcdb9d0 100644
--- a/com32/lib/syslinux/shuffle.c
+++ b/com32/lib/syslinux/shuffle.c
@@ -38,6 +38,7 @@
#include <string.h>
#include <inttypes.h>
#include <com32.h>
+#include <core.h>
#include <minmax.h>
#include <dprintf.h>
#include <syslinux/movebits.h>
@@ -51,12 +52,8 @@ static int shuffler_size;
static void __constructor __syslinux_get_shuffer_size(void)
{
- static com32sys_t reg;
-
- reg.eax.w[0] = 0x0023;
- __intcall(0x22, &reg, &reg);
-
- shuffler_size = (reg.eflags.l & EFLAGS_CF) ? 2048 : reg.ecx.w[0];
+ /* +15 padding is to guarantee alignment */
+ shuffler_size = __bcopyxx_len + 15;
}
/*
@@ -135,7 +132,7 @@ int syslinux_do_shuffle(struct syslinux_movelist *fraglist,
goto bail;
#if DEBUG > 1
- syslinux_dump_movelist(stdout, fraglist);
+ syslinux_dump_movelist(fraglist);
#endif
if (syslinux_compute_movelist(&moves, fraglist, rxmap))
@@ -155,7 +152,7 @@ int syslinux_do_shuffle(struct syslinux_movelist *fraglist,
#if DEBUG > 1
dprintf("Final movelist:\n");
- syslinux_dump_movelist(stdout, moves);
+ syslinux_dump_movelist(moves);
#endif
syslinux_free_memmap(rxmap);
diff --git a/com32/lib/syslinux/version.c b/com32/lib/syslinux/version.c
index 15b617b0..1cd2efd3 100644
--- a/com32/lib/syslinux/version.c
+++ b/com32/lib/syslinux/version.c
@@ -27,20 +27,23 @@
#include <syslinux/config.h>
#include <klibc/compiler.h>
-#include <com32.h>
+#include <core.h>
+#include <../../../version.h>
struct syslinux_version __syslinux_version;
void __constructor __syslinux_get_version(void)
{
- static com32sys_t reg;
+ __syslinux_version.version = (VERSION_MAJOR << 8) + VERSION_MINOR;
- reg.eax.w[0] = 0x0001;
- __intcall(0x22, &reg, &reg);
+ /* We no longer support the COMBOOT API */
+ __syslinux_version.max_api = 0xffff;
- __syslinux_version.version = reg.ecx.w[0];
- __syslinux_version.max_api = reg.eax.w[0];
- __syslinux_version.filesystem = reg.edx.b[0];
- __syslinux_version.version_string = MK_PTR(reg.es, reg.esi.w[0]);
- __syslinux_version.copyright_string = MK_PTR(reg.es, reg.edi.w[0]);
+ __syslinux_version.filesystem = syslinux_filesystem();
+
+ /* Skip leading CR LF */
+ __syslinux_version.version_string = &syslinux_banner[2];
+
+ /* Skip leading space */
+ __syslinux_version.copyright_string = &copyright_str[1];
}
diff --git a/com32/lib/syslinux/video/fontquery.c b/com32/lib/syslinux/video/fontquery.c
index dd5d86e3..ac1fab3f 100644
--- a/com32/lib/syslinux/video/fontquery.c
+++ b/com32/lib/syslinux/video/fontquery.c
@@ -31,24 +31,18 @@
*/
#include <syslinux/video.h>
-#include <com32.h>
+#include <graphics.h>
/*
* Returns height of font or zero if no custom font loaded
*/
int syslinux_font_query(uint8_t **font)
{
- static com32sys_t ireg;
- com32sys_t oreg;
- int height;
+ if (!UserFont)
+ return 0;
- ireg.eax.w[0] = 0x0018;
- __intcall(0x22, &ireg, &oreg);
+ *font = (uint8_t *)fontbuf;
- height = !(oreg.eflags.l & EFLAGS_CF) ? oreg.eax.b[0] : 0;
- if (height)
- *font = MK_PTR(oreg.es, oreg.ebx.w[0]);
-
- return height;
+ return VGAFontSize;
}
diff --git a/com32/lib/syslinux/video/reportmode.c b/com32/lib/syslinux/video/reportmode.c
index 57fd6fdc..2a2c5770 100644
--- a/com32/lib/syslinux/video/reportmode.c
+++ b/com32/lib/syslinux/video/reportmode.c
@@ -31,15 +31,12 @@
*/
#include <syslinux/video.h>
-#include <com32.h>
+#include <graphics.h>
void syslinux_report_video_mode(uint16_t flags, uint16_t xsize, uint16_t ysize)
{
- static com32sys_t ireg;
+ if (flags > 0x0f)
+ return;
- ireg.eax.w[0] = 0x0017;
- ireg.ebx.w[0] = flags;
- ireg.ecx.w[0] = xsize;
- ireg.edx.w[0] = ysize;
- __intcall(0x22, &ireg, NULL);
+ using_vga(flags, xsize, ysize);
}
diff --git a/com32/lib/vdprintf.c b/com32/lib/vdprintf.c
index c1f90a63..bcf55bb7 100644
--- a/com32/lib/vdprintf.c
+++ b/com32/lib/vdprintf.c
@@ -10,11 +10,7 @@
#include <sys/io.h>
#include <sys/cpu.h>
-#undef DEBUG
-#define DEBUG 1
-#include <dprintf.h>
-
-#ifndef vdprintf
+#ifdef DEBUG_PORT
#define BUFFER_SIZE 4096
@@ -33,10 +29,6 @@ enum serial_port_regs {
SCR = 7,
};
-#ifndef DEBUG_PORT
-# define DEBUG_PORT 0x03f8 /* I/O base address */
-#endif
-
static const uint16_t debug_base = DEBUG_PORT;
static void debug_putc(char c)
@@ -58,7 +50,6 @@ void vdprintf(const char *format, va_list ap)
static bool debug_ok = false;
rv = vsnprintf(buffer, BUFFER_SIZE, format, ap);
-
if (rv < 0)
return;
@@ -117,4 +108,4 @@ void vdprintf(const char *format, va_list ap)
debug_putc(*p++);
}
-#endif /* vdprintf */
+#endif /* DEBUG_PORT */
diff --git a/com32/lib/zalloc.c b/com32/lib/zalloc.c
deleted file mode 100644
index 0e6ed28d..00000000
--- a/com32/lib/zalloc.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * zalloc.c
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-void *zalloc(size_t size)
-{
- void *ptr;
-
- ptr = malloc(size);
- if (ptr)
- memset(ptr, 0, size);
-
- return ptr;
-}
diff --git a/com32/libupload/upload_tftp.c b/com32/libupload/upload_tftp.c
index 5e73c1c5..6a0dacb7 100644
--- a/com32/libupload/upload_tftp.c
+++ b/com32/libupload/upload_tftp.c
@@ -53,7 +53,6 @@ const char *tftp_string_error_message[]={
static int send_ack_packet(struct tftp_state *tftp,
const void *pkt, size_t len)
{
- com32sys_t ireg, oreg;
t_PXENV_UDP_WRITE *uw;
t_PXENV_UDP_READ *ur;
clock_t start;
@@ -67,9 +66,6 @@ static int send_ack_packet(struct tftp_state *tftp,
uw = lmalloc(sizeof *uw + len);
ur = lmalloc(sizeof *ur + RCV_BUF);
- memset(&ireg, 0, sizeof ireg);
- ireg.eax.w[0] = 0x0009;
-
for (timeout = timeouts ; *timeout ; timeout++) {
memset(uw, 0, sizeof *uw);
memcpy(uw+1, pkt, len);
@@ -80,11 +76,7 @@ static int send_ack_packet(struct tftp_state *tftp,
uw->buffer_size = len;
uw->buffer = FAR_PTR(uw+1);
- ireg.ebx.w[0] = PXENV_UDP_WRITE;
- ireg.es = SEG(uw);
- ireg.edi.w[0] = OFFS(uw);
-
- __intcall(0x22, &ireg, &oreg);
+ pxe_call(PXENV_UDP_WRITE, uw);
start = times(NULL);
@@ -97,13 +89,9 @@ static int send_ack_packet(struct tftp_state *tftp,
ur->buffer_size = RCV_BUF;
ur->buffer = FAR_PTR(ur+1);
- ireg.ebx.w[0] = PXENV_UDP_READ;
- ireg.es = SEG(ur);
- ireg.edi.w[0] = OFFS(ur);
- __intcall(0x22, &ireg, &oreg);
+ err = pxe_call(PXENV_UDP_READ, ur);
- if (!(oreg.eflags.l & EFLAGS_CF) &&
- ur->status == PXENV_STATUS_SUCCESS &&
+ if (!err && ur->status == PXENV_STATUS_SUCCESS &&
tftp->srv_ip == ur->src_ip &&
(tftp->srv_port == 0 ||
tftp->srv_port == ur->s_port)) {
diff --git a/com32/libutil/Makefile b/com32/libutil/Makefile
index 83e23a0a..5aa7ceb8 100644
--- a/com32/libutil/Makefile
+++ b/com32/libutil/Makefile
@@ -31,19 +31,18 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
-LIBOBJS = ansiline.o ansiraw.o get_key.o keyname.o \
+LIBOBJS = ansiline.o ansiraw.o keyname.o \
sha1hash.o unbase64.o \
- md5.o crypt-md5.o sha256crypt.o sha512crypt.o base64.o
+ md5.o crypt-md5.o sha256crypt.o sha512crypt.o base64.o \
+ quicksort.o
LNXLIBOBJS = $(patsubst %.o,%.lo,$(LIBOBJS))
-all: libutil_com.a libutil_lnx.a
+all: libutil.c32 libutil_lnx.a
-libutil_com.a: $(LIBOBJS)
- rm -f $@
- $(AR) cq $@ $(LIBOBJS)
- $(RANLIB) $@
+libutil.elf: $(LIBOBJS)
+ $(LD) $(LDFLAGS) -soname $(patsubst %.elf,%.c32,$(@F)) -o $@ $^
libutil_lnx.a: $(LNXLIBOBJS)
rm -f $@
@@ -62,6 +61,6 @@ spotless: clean
install: all
mkdir -m 755 -p $(INSTALLROOT)$(COM32DIR)
- install -m 644 libutil_com.a libutil_lnx.a $(INSTALLROOT)$(COM32DIR)
+ install -m 644 libutil_lnx.a $(INSTALLROOT)$(COM32DIR)
-include .*.d
diff --git a/com32/libutil/ansiraw.c b/com32/libutil/ansiraw.c
index 2afd48a7..b67768c5 100644
--- a/com32/libutil/ansiraw.c
+++ b/com32/libutil/ansiraw.c
@@ -47,6 +47,7 @@ void console_ansi_raw(void)
#include <stdio.h>
#include <termios.h>
+#include <unistd.h>
static struct termios original_termios_settings;
@@ -82,4 +83,22 @@ void console_ansi_raw(void)
tcsetattr(0, TCSAFLUSH, &tio);
}
+int raw_read(int fd, void *buf, size_t count)
+{
+ struct termios tio, rtio;
+ int rv;
+
+ tcgetattr(fd, &tio);
+
+ cfmakeraw(&rtio);
+ tcsetattr(fd, 0, &rtio);
+
+ rv = read(fd, buf, count);
+
+ /* Restore settings */
+ tcsetattr(fd, 0, &tio);
+
+ return rv;
+}
+
#endif
diff --git a/com32/libutil/include/getkey.h b/com32/libutil/include/getkey.h
index a46de812..0733723b 100644
--- a/com32/libutil/include/getkey.h
+++ b/com32/libutil/include/getkey.h
@@ -77,8 +77,11 @@
#define KEY_MAX 0x012a
+#define KEY_MAXLEN 8
+
int get_key(FILE *, clock_t);
int key_name_to_code(const char *);
const char *key_code_to_name(int);
+int get_key_decode(char *, int, int *);
#endif /* LIBUTIL_GETKEY_H */
diff --git a/com32/libutil/quicksort.c b/com32/libutil/quicksort.c
new file mode 100644
index 00000000..32ac0f01
--- /dev/null
+++ b/com32/libutil/quicksort.c
@@ -0,0 +1,59 @@
+/*
+ * sort.c - Sample ELF module providing a quick sort function
+ *
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#include <stdlib.h>
+
+static inline void swap(int *x, int *y)
+{
+ int tmp;
+ tmp = *x;
+ *x = *y;
+ *y = tmp;
+}
+
+static inline int randint(int l, int u)
+{
+ return l + (rand() % (u - l + 1));
+}
+
+/**
+ * quick_sort_range - A small and efficient version of quicksort.
+ * @nums: The numbers to sort
+ * @l: The lower index in the vector (inclusive)
+ * @u: The upper index in the vector (inclusive)
+ *
+ * The implementation is taken from "Beautiful Code", by O'Reilly, the
+ * book students received from Google as a gift for their acceptance
+ * in the GSoC 2008 program. The code belongs to Jon Bentley, who
+ * wrote the third chapter of the book. Since ELF modules were written
+ * as part of this program, the author of the module considered
+ * the book had to be put to some use. :)
+ */
+static void quick_sort_range(int *nums, int l, int u)
+{
+ int i, m;
+ if (l >= u)
+ return;
+
+ swap(&nums[l], &nums[randint(l, u)]);
+
+ m = l;
+ for (i = l + 1; i <= u; i++) {
+ if (nums[i] < nums[l])
+ swap(&nums[++m], &nums[i]);
+ }
+
+ swap(&nums[l], &nums[m]);
+
+ quick_sort_range(nums, l, m - 1);
+ quick_sort_range(nums, m + 1, u);
+}
+
+void quick_sort(int *nums, int count)
+{
+ quick_sort_range(nums, 0, count - 1);
+}
diff --git a/com32/lua/etc/luavs.bat b/com32/lua/etc/luavs.bat
index 08c2bedd..054b4625 100644
--- a/com32/lua/etc/luavs.bat
+++ b/com32/lua/etc/luavs.bat
@@ -1,28 +1,28 @@
-@rem Script to build Lua under "Visual Studio .NET Command Prompt".
-@rem Do not run from this directory; run it from the toplevel: etc\luavs.bat .
-@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.
-@rem (contributed by David Manura and Mike Pall)
-
-@setlocal
-@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE
-@set MYLINK=link /nologo
-@set MYMT=mt /nologo
-
-cd src
-%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c
-del lua.obj luac.obj
-%MYLINK% /DLL /out:lua51.dll l*.obj
-if exist lua51.dll.manifest^
- %MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
-%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c
-%MYLINK% /out:lua.exe lua.obj lua51.lib
-if exist lua.exe.manifest^
- %MYMT% -manifest lua.exe.manifest -outputresource:lua.exe
-%MYCOMPILE% l*.c print.c
-del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^
- loslib.obj ltablib.obj lstrlib.obj loadlib.obj
-%MYLINK% /out:luac.exe *.obj
-if exist luac.exe.manifest^
- %MYMT% -manifest luac.exe.manifest -outputresource:luac.exe
-del *.obj *.manifest
-cd ..
+@rem Script to build Lua under "Visual Studio .NET Command Prompt".
+@rem Do not run from this directory; run it from the toplevel: etc\luavs.bat .
+@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.
+@rem (contributed by David Manura and Mike Pall)
+
+@setlocal
+@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE
+@set MYLINK=link /nologo
+@set MYMT=mt /nologo
+
+cd src
+%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c
+del lua.obj luac.obj
+%MYLINK% /DLL /out:lua51.dll l*.obj
+if exist lua51.dll.manifest^
+ %MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
+%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c
+%MYLINK% /out:lua.exe lua.obj lua51.lib
+if exist lua.exe.manifest^
+ %MYMT% -manifest lua.exe.manifest -outputresource:lua.exe
+%MYCOMPILE% l*.c print.c
+del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^
+ loslib.obj ltablib.obj lstrlib.obj loadlib.obj
+%MYLINK% /out:luac.exe *.obj
+if exist luac.exe.manifest^
+ %MYMT% -manifest luac.exe.manifest -outputresource:luac.exe
+del *.obj *.manifest
+cd ..
diff --git a/com32/lua/src/Makefile b/com32/lua/src/Makefile
index f03f7a7f..d70d23ea 100644
--- a/com32/lua/src/Makefile
+++ b/com32/lua/src/Makefile
@@ -17,7 +17,7 @@
topdir = ../../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
LNXLIBS =
@@ -30,7 +30,7 @@ TESTFILES =
OBJS = lua.o
-LIBLUA = liblua.a
+LIBLUA = liblua.c32
LIBLUA_OBJS := lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o
LIBLUA_OBJS += lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o
@@ -50,16 +50,15 @@ CFLAGS += -DLUA_ANSI
all: $(MODULES) $(TESTFILES)
-$(LIBLUA) : $(LIBLUA_OBJS)
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
+liblua.elf : $(LIBLUA_OBJS)
+ $(LD) $(LDFLAGS) -shared -soname $(patsubst %.elf,%.c32,$(@F)) \
+ -o $@ $^
lua.elf : $(OBJS) $(LIBLUA) $(C_LIBS)
$(LD) $(LDFLAGS) -o $@ $^
tidy dist:
- rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
+ rm -f *.o *.lo *.lst *.elf .*.d *.tmp
clean: tidy
rm -f *.lnx
diff --git a/com32/lua/src/vesa.c b/com32/lua/src/vesa.c
index 9f281348..06649e11 100644
--- a/com32/lua/src/vesa.c
+++ b/com32/lua/src/vesa.c
@@ -17,10 +17,15 @@ static int vesa_getmodes(lua_State *L)
struct vesa_general_info *gi;
struct vesa_mode_info *mi;
int nmode = 1;
+ int rv = -1;
- /* Allocate space in the bounce buffer for these structures */
- gi = &((struct vesa_info *)__com32.cs_bounce)->gi;
- mi = &((struct vesa_info *)__com32.cs_bounce)->mi;
+ gi = lmalloc(sizeof *gi);
+ if (!gi)
+ return -1;
+
+ mi = lmalloc(sizeof *mi);
+ if (!mi)
+ goto out;
memset(&rm, 0, sizeof rm);
memset(gi, 0, sizeof *gi);
@@ -32,11 +37,15 @@ static int vesa_getmodes(lua_State *L)
__intcall(0x10, &rm, &rm);
if ( rm.eax.w[0] != 0x004F )
- return -1; /* Function call failed */
- if ( gi->signature != VESA_MAGIC )
- return -2; /* No magic */
- if ( gi->version < 0x0102 )
- return -3; /* VESA 1.2+ required */
+ goto out; /* Function call failed */
+ if ( gi->signature != VESA_MAGIC ) {
+ rv = -2; /* No magic */
+ goto out;
+ }
+ if ( gi->version < 0x0102 ) {
+ rv = -3; /* VESA 1.2+ required */
+ goto out;
+ }
lua_newtable(L); /* list of modes */
@@ -86,7 +95,11 @@ static int vesa_getmodes(lua_State *L)
}
- return 1;
+ rv = 1;
+out:
+ lfree(mi);
+ lfree(gi);
+ return rv;
}
diff --git a/com32/mboot/Makefile b/com32/mboot/Makefile
index b7ee1154..6e010b1c 100644
--- a/com32/mboot/Makefile
+++ b/com32/mboot/Makefile
@@ -17,7 +17,7 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
LNXLIBS = ../libutil/libutil_lnx.a
diff --git a/com32/mboot/initvesa.c b/com32/mboot/initvesa.c
index cf2707df..bd869e3d 100644
--- a/com32/mboot/initvesa.c
+++ b/com32/mboot/initvesa.c
@@ -38,6 +38,7 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
+#include <graphics.h>
#include "vesa.h"
#include "mboot.h"
@@ -61,9 +62,13 @@ void set_graphics_mode(const struct multiboot_header *mbh,
if (!(mbh->flags & MULTIBOOT_VIDEO_MODE) || mbh->mode_type != 0)
return;
- /* Allocate space in the bounce buffer for these structures */
- gi = &((struct vesa_info *)__com32.cs_bounce)->gi;
- mi = &((struct vesa_info *)__com32.cs_bounce)->mi;
+ gi = lmalloc(sizeof *gi);
+ if (!gi)
+ return;
+
+ mi = lmalloc(sizeof *mi);
+ if (!mi)
+ goto out;
memset(&rm, 0, sizeof rm);
memset(gi, 0, sizeof *gi);
@@ -75,11 +80,11 @@ void set_graphics_mode(const struct multiboot_header *mbh,
__intcall(0x10, &rm, &rm);
if (rm.eax.w[0] != 0x004F)
- return; /* Function call failed */
+ goto out; /* Function call failed */
if (gi->signature != VESA_MAGIC)
- return; /* No magic */
+ goto out; /* No magic */
if (gi->version < 0x0102)
- return; /* VESA 1.2+ required */
+ goto out; /* VESA 1.2+ required */
memcpy(&vesa_info.gi, gi, sizeof *gi);
@@ -182,7 +187,7 @@ void set_graphics_mode(const struct multiboot_header *mbh,
}
if (!bestpxf)
- return; /* No mode found */
+ goto out; /* No mode found */
mi = &vesa_info.mi;
mode = bestmode;
@@ -193,7 +198,7 @@ void set_graphics_mode(const struct multiboot_header *mbh,
rm.ebx.w[0] = mode;
__intcall(0x10, &rm, &rm);
if (rm.eax.w[0] != 0x004F)
- return; /* Failed to set mode */
+ goto out; /* Failed to set mode */
mbi->flags |= MB_INFO_VIDEO_INFO;
mbi->vbe_mode = mode;
@@ -211,16 +216,16 @@ void set_graphics_mode(const struct multiboot_header *mbh,
mbi->vbe_interface_len = rm.ecx.w[0];
}
- /* Tell syslinux we changed video mode */
- rm.eax.w[0] = 0x0017; /* Report video mode change */
/* In theory this should be:
-
- rm.ebx.w[0] = (mi->mode_attr & 4) ? 0x0007 : 0x000f;
-
- However, that would assume all systems that claim to handle text
- output in VESA modes actually do that... */
- rm.ebx.w[0] = 0x000f;
- rm.ecx.w[0] = vesa_info.mi.h_res;
- rm.edx.w[0] = vesa_info.mi.v_res;
- __intcall(0x22, &rm, NULL);
+ *
+ * UsingVga = (mi->mode_attr & 4) ? 0x0007 : 0x000f;
+ *
+ * However, that would assume all systems that claim to handle text
+ * output in VESA modes actually do that...
+ */
+ graphics_using_vga(0x0F, vesa_info.mi.h_res, vesa_info.mi.v_res);
+
+out:
+ lfree(mi);
+ lfree(gi);
}
diff --git a/com32/mboot/mem.c b/com32/mboot/mem.c
index 6a31fac0..6e3995bf 100644
--- a/com32/mboot/mem.c
+++ b/com32/mboot/mem.c
@@ -49,9 +49,10 @@ struct e820_entry {
static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
{
com32sys_t ireg, oreg;
- struct e820_entry *e820buf = __com32.cs_bounce;
+ struct e820_entry *e820buf;
struct AddrRangeDesc *ard;
size_t ard_count, ard_space;
+ int rv = 0;
/* Use INT 12h to get DOS memory */
__intcall(0x12, &__com32_zero_regs, &oreg);
@@ -65,10 +66,14 @@ static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
*dosmem = 640 * 1024; /* Hope for the best... */
}
+ e820buf = lmalloc(sizeof(*e820buf));
+ if (!e820buf)
+ return 0;
+
/* Allocate initial space */
*ardp = ard = malloc(RANGE_ALLOC_BLOCK * sizeof *ard);
if (!ard)
- return 0;
+ goto out;
ard_count = 0;
ard_space = RANGE_ALLOC_BLOCK;
@@ -93,8 +98,10 @@ static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
if (ard_count >= ard_space) {
ard_space += RANGE_ALLOC_BLOCK;
*ardp = ard = realloc(ard, ard_space * sizeof *ard);
- if (!ard)
- return ard_count;
+ if (!ard) {
+ rv = ard_count;
+ goto out;
+ }
}
ard[ard_count].size = 20;
@@ -106,8 +113,10 @@ static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
ireg.ebx.l = oreg.ebx.l;
} while (oreg.ebx.l);
- if (ard_count)
- return ard_count;
+ if (ard_count) {
+ rv = ard_count;
+ goto out;
+ };
ard[0].size = 20;
ard[0].BaseAddr = 0;
@@ -129,10 +138,12 @@ static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
ard[2].BaseAddr = 16 << 20;
ard[2].Length = oreg.edx.w[0] << 16;
ard[2].Type = 1;
- return 3;
+ rv = 3;
} else {
- return 2;
+ rv = 2;
}
+
+ goto out;
}
/* Finally try INT 15h AH=88h */
@@ -142,10 +153,14 @@ static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
ard[1].BaseAddr = 1 << 20;
ard[1].Length = oreg.ecx.w[0] << 10;
ard[1].Type = 1;
- return 2;
+ rv = 2;
+ goto out;
}
- return 1; /* ... problematic ... */
+ rv = 1; /* ... problematic ... */
+out:
+ lfree(e820buf);
+ return rv;
}
void mboot_make_memmap(void)
diff --git a/com32/menu/Makefile b/com32/menu/Makefile
index b67b997d..e62c6b87 100644
--- a/com32/menu/Makefile
+++ b/com32/menu/Makefile
@@ -16,7 +16,7 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
LNXLIBS = ../libutil/libutil_lnx.a
@@ -24,7 +24,7 @@ MODULES = menu.c32 vesamenu.c32
TESTFILES =
COMMONOBJS = menumain.o readconfig.o passwd.o drain.o printmsg.o colors.o \
- background.o refstr.o execute.o
+ background.o refstr.o
all: $(MODULES) $(TESTFILES)
diff --git a/com32/menu/execute.c b/com32/menu/execute.c
deleted file mode 100644
index c2de7353..00000000
--- a/com32/menu/execute.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston MA 02110-1301, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <com32.h>
-#include "menu.h"
-
-void execute(const char *cmdline, enum kernel_type type)
-{
- com32sys_t ireg;
- const char *p, *const *pp;
- char *q = __com32.cs_bounce;
- const char *kernel, *args;
-
- memset(&ireg, 0, sizeof ireg);
-
- kernel = q;
- p = cmdline;
- while (*p && !my_isspace(*p)) {
- *q++ = *p++;
- }
- *q++ = '\0';
-
- args = q;
- while (*p && my_isspace(*p))
- p++;
-
- strcpy(q, p);
-
- if (kernel[0] == '.' && type == KT_NONE) {
- /* It might be a type specifier */
- enum kernel_type type = KT_NONE;
- for (pp = kernel_types; *pp; pp++, type++) {
- if (!strcmp(kernel + 1, *pp)) {
- execute(p, type); /* Strip the type specifier and retry */
- }
- }
- }
-
- if (type == KT_LOCALBOOT) {
- ireg.eax.w[0] = 0x0014; /* Local boot */
- ireg.edx.w[0] = strtoul(kernel, NULL, 0);
- } else {
- if (type < KT_KERNEL)
- type = KT_KERNEL;
-
- ireg.eax.w[0] = 0x0016; /* Run kernel image */
- ireg.esi.w[0] = OFFS(kernel);
- ireg.ds = SEG(kernel);
- ireg.ebx.w[0] = OFFS(args);
- ireg.es = SEG(args);
- ireg.edx.l = type - KT_KERNEL;
- /* ireg.ecx.l = 0; *//* We do ipappend "manually" */
- }
-
- __intcall(0x22, &ireg, NULL);
-
- /* If this returns, something went bad; return to menu */
-}
diff --git a/com32/menu/menumain.c b/com32/menu/menumain.c
index 8573901c..a3061ede 100644
--- a/com32/menu/menumain.c
+++ b/com32/menu/menumain.c
@@ -28,7 +28,9 @@
#include <setjmp.h>
#include <limits.h>
#include <com32.h>
+#include <core.h>
#include <syslinux/adv.h>
+#include <syslinux/boot.h>
#include "menu.h"
@@ -1157,9 +1159,13 @@ int main(int argc, char *argv[])
printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
if (cmdline) {
- execute(cmdline, KT_NONE);
- if (cm->onerror)
- execute(cm->onerror, KT_NONE);
+ uint32_t type = parse_image_type(cmdline);
+
+ execute(cmdline, type, false);
+ if (cm->onerror) {
+ type = parse_image_type(cm->onerror);
+ execute(cm->onerror, type, true);
+ }
} else {
return 0; /* Exit */
}
diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c
index 431017f6..b7814be2 100644
--- a/com32/menu/readconfig.c
+++ b/com32/menu/readconfig.c
@@ -62,7 +62,7 @@ static const struct messages messages[MSG_COUNT] = {
__p; })
/* Must match enum kernel_type */
-const char *const kernel_types[] = {
+static const char *const kernel_types[] = {
"none",
"localboot",
"kernel",
@@ -288,6 +288,31 @@ static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
}
}
+/*
+ * Copy a string, converting whitespace characters to underscores
+ * and compacting them. Return a pointer to the final null.
+ */
+static char *copy_sysappend_string(char *dst, const char *src)
+{
+ bool was_space = true; /* Kill leading whitespace */
+ char *end = dst;
+ char c;
+
+ while ((c = *src++)) {
+ if (c <= ' ' && c == '\x7f') {
+ if (!was_space)
+ *dst++ = '_';
+ was_space = true;
+ } else {
+ *dst++ = c;
+ end = dst;
+ was_space = false;
+ }
+ }
+ *end = '\0';
+ return end;
+}
+
static void record(struct menu *m, struct labeldata *ld, const char *append)
{
int i;
@@ -353,9 +378,11 @@ static void record(struct menu *m, struct labeldata *ld, const char *append)
if (ld->ipappend) {
ipappend = syslinux_ipappend_strings();
for (i = 0; i < ipappend->count; i++) {
- if ((ld->ipappend & (1U << i)) && ipappend->ptr[i] &&
- ipappend->ptr[i][0])
- ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
+ if ((ld->ipappend & (1U << i)) &&
+ ipappend->ptr[i] && ipappend->ptr[i][0]) {
+ *ipp++ = ' ';
+ ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
+ }
}
}
@@ -1017,11 +1044,13 @@ do_include:
m->ontimeout = refstrdup(skipspace(p + 9));
} else if (looking_at(p, "allowoptions")) {
m->allowedit = !!atoi(skipspace(p + 12));
- } else if (looking_at(p, "ipappend")) {
+ } else if ((ep = looking_at(p, "ipappend")) ||
+ (ep = looking_at(p, "sysappend"))) {
+ uint32_t s = strtoul(skipspace(ep), NULL, 0);
if (ld.label)
- ld.ipappend = atoi(skipspace(p + 8));
+ ld.ipappend = s;
else
- ipappend = atoi(skipspace(p + 8));
+ ipappend = s;
} else if (looking_at(p, "default")) {
refstr_put(globaldefault);
globaldefault = refstrdup(skipspace(p + 7));
diff --git a/com32/modules/Makefile b/com32/modules/Makefile
index f110e584..d801a260 100644
--- a/com32/modules/Makefile
+++ b/com32/modules/Makefile
@@ -17,33 +17,25 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
MODULES = config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \
disk.c32 pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 \
meminfo.c32 sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 \
kbdmap.c32 cmd.c32 vpdtest.c32 host.c32 ls.c32 gpxecmd.c32 \
ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 \
- whichsys.c32 prdhcp.c32 pxechn.c32 kontron_wdt.c32 ifmemdsk.c32
+ whichsys.c32 prdhcp.c32 pxechn.c32 kontron_wdt.c32 ifmemdsk.c32 \
+ hexdump.c32 poweroff.c32 cptime.c32 debug.c32
TESTFILES =
all: $(MODULES) $(TESTFILES)
-pcitest.elf : pcitest.o $(LIBS) $(C_LIBS)
- $(LD) $(LDFLAGS) -o $@ $^
-
-cpuidtest.elf : cpuidtest.o $(GPLLIB) $(LIBS) $(C_LIBS)
- $(LD) $(LDFLAGS) -o $@ $^
-
.PRECIOUS: %.o
dmitest.o: dmitest.c
$(CC) $(CFLAGS) $(GPLINCLUDE) -c -o $@ $<
-dmitest.elf : dmi_utils.o dmitest.o $(GPLLIB) $(LIBS) $(C_LIBS)
- $(LD) $(LDFLAGS) -o $@ $^
-
-ethersel.elf : ethersel.o $(LIBS) $(C_LIBS)
+dmitest.elf : dmi_utils.o dmitest.o $(C_LIBS)
$(LD) $(LDFLAGS) -o $@ $^
tidy dist:
diff --git a/com32/modules/cat.c b/com32/modules/cat.c
index 0a9514c4..2a7683f2 100644
--- a/com32/modules/cat.c
+++ b/com32/modules/cat.c
@@ -9,8 +9,6 @@ int main(int argc, char *argv[])
int len;
char buf[4096];
- openconsole(&dev_stdcon_r, &dev_stdcon_w);
-
if (argc < 2) {
fprintf(stderr, "Usage: %s filename...\n", argv[0]);
return 1;
diff --git a/com32/modules/cmd.c b/com32/modules/cmd.c
index 5d3f8918..233c7cac 100644
--- a/com32/modules/cmd.c
+++ b/com32/modules/cmd.c
@@ -21,6 +21,6 @@
int main(void)
{
- syslinux_run_command(__com32.cs_cmdline);
+ syslinux_run_command(com32_cmdline());
return -1;
}
diff --git a/com32/modules/config.c b/com32/modules/config.c
index 334a635c..04cb0ab2 100644
--- a/com32/modules/config.c
+++ b/com32/modules/config.c
@@ -24,8 +24,6 @@
int main(int argc, char *argv[])
{
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc < 2 || argc > 3) {
fprintf(stderr, "Usage: config <filename> [<directory>]\n");
return 1;
diff --git a/com32/modules/cptime.c b/com32/modules/cptime.c
new file mode 100644
index 00000000..0f5ffe61
--- /dev/null
+++ b/com32/modules/cptime.c
@@ -0,0 +1,284 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2010-2011 Gene Cumm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * cptime.c Version 1.4
+ *
+ * Timed copy; read entire file then output total time, bytes transferred,
+ * and compute transfer rate.
+ *
+ * cptime [-s|-l] [-v|-q] [-b _SIZE_] [-n _LEN_] _FILE_...
+ * -s Change to simple output mode without computing transfer rate
+ * -l Change to long output mode (to allow for overriding previous -s)
+ * -v Verbose output
+ * -q Quiet output
+ * -b _SIZE_ use _SIZE_ for transfer size
+ * -n _LEN_ maximum length to fetch
+ * _FILE_... Space delimited list of files to dump
+ * Note: The last instance of -s or -l wins, along with the last use of -b and -n and the winning option will be applied to all operations
+ *
+ * Hisory:
+ * 1.4 Use fread() rather than read(); use CLK_TCK when available.
+ * 1.3 Added -v/-q; rework some argument processing.
+ * 1.2 Added -n
+ * 1.1 Added -l and -b switches; more flexible command line processing
+ * 1.0 First release
+ */
+
+/*
+ * ToDos:
+ * - Refine timing to be more precise. Low priority.
+ * - Add -o for offset. Wishlist.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <consoles.h>
+#include <minmax.h>
+#include <limits.h>
+#include <string.h>
+#include <stdint.h>
+#include <console.h>
+
+#ifdef __COM32__
+# define BUFSZ_DEF (size_t)2048
+/* What's optimal? Under 4k?
+ * layout.inc: xfer_buf_seg equ 1000h
+ * com32.inc: push dword (1 << 16) ; 64K bounce buffer
+ */
+/* typedef size_t off_t */
+
+# define TPS_T float
+# ifdef CLK_TCK
+static inline TPS_T get_tps(void) { return CLK_TCK; }
+# else
+static inline TPS_T get_tps(void) { return 18.2; }
+# endif
+
+#else /* __COM32__ */
+
+# define BUFSZ_DEF (size_t)16384
+/* Need to check what might be a "best" buffer/fetch block size here */
+
+# define TPS_T long
+static inline TPS_T get_tps(void) { return sysconf(_SC_CLK_TCK); }
+
+#endif /* __COM32__ */
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX PTRDIFF_MAX
+#endif
+/* typedef ptrdiff_t ssize_t; */
+#define BUFSZ_MAX (size_t)SSIZE_MAX
+/* ssize_t max */
+#define BUFSZ_MIN (size_t)1
+
+
+/* Please note: I don't know the origin of these two macros nor their license */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+#define TYPE_MAX(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) -1 \
+ : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
+
+#ifndef OFF_T_MAX
+# define OFF_T_MAX TYPE_MAX(off_t)
+#endif
+/* Can't be SIZE_MAX or SSIZE_MAX as Syslinux/COM32 is unsigned while Linux
+ * is signed.
+ */
+
+#define LEN_MAX OFF_T_MAX
+/* off_t max */
+#define LEN_MIN (off_t)0
+
+void print_cp_result_tick(size_t bcnt, clock_t et, TPS_T tps, int offs)
+{
+ size_t dr;
+ /* prevent divide by 0 */
+ dr = max(bcnt, (bcnt * tps)) / max((clock_t)1, (et + offs));
+ printf(" %+d %zu B/s; %zu KiB/s; %zu MiB/s\n", offs, dr, dr/1024, dr/1048576);
+} /* void print_cp_result_tick(size_t bcnt, clock_t et, TPS_T tps, int offs) */
+
+void print_cp_result_long(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz, char do_verbose)
+{
+ TPS_T tps;
+ if (do_verbose > 2)
+ printf("Enter print_cp_result_long()\n");
+ tps = get_tps();
+ printf(" %zu B in %d ticks from '%s'\n", bcnt, (int)(ec - bc), fn);
+ printf(" ~%d ticks per second; %zu B block/transfer size\n", (int)tps, bufsz);
+ print_cp_result_tick(bcnt, (ec - bc), tps, 0);
+ print_cp_result_tick(bcnt, (ec - bc), tps, 1);
+ print_cp_result_tick(bcnt, (ec - bc), tps, -1);
+} /* void print_cp_result_long(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz) */
+
+void print_cp_result_simple(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz, char do_verbose)
+{
+ if (do_verbose) {}
+ printf(" %zuB %dt %zux '%s'\n", bcnt, (int)(ec - bc), bufsz, fn);
+} /* void print_cp_result_simple(char *fn, int bcnt, clock_t bc, clock_t ec, char do_verbose) */
+
+size_t time_copy_bufsz(size_t bufsz, size_t bcnt, off_t maxlen)
+{
+ return min(bufsz, (maxlen - bcnt));
+} /* size_t time_copy_bufsz(size_t bufsz, size_t bcnt, off_t maxlen) */
+
+int time_copy(char *fn, char do_simple, char do_verbose, size_t ibufsz, off_t maxlen)
+{
+// int fd;
+ int rv = 0;
+ int i = 0;
+ FILE *f;
+ size_t bufsz, bcnt = 0;
+ int numrd;
+ struct tms tm;
+ clock_t bc, ec;
+ char buf[ibufsz + 1];
+
+ buf[0] = 0;
+ if (do_verbose)
+ printf("Trying file '%s'\n", fn);
+ errno = 0;
+// fd = open(fn, O_RDONLY);
+ f = fopen(fn, "r");
+// if (fd == -1) {
+ if (!f) {
+ switch (errno) {
+ case ENOENT :
+ printf("File '%s' does not exist\n", fn);
+ break;
+ case EBADF:
+ printf("File '%s': Bad File Descriptor\n", fn);
+ break;
+ default :
+ printf("Error '%d' opening file '%s'\n", errno, fn);
+ }
+ rv = 1;
+ } else {
+ if (do_verbose)
+ printf("File '%s' opened\n", fn);
+ bufsz = time_copy_bufsz(ibufsz, bcnt, maxlen);
+ bc = times(&tm);
+// numrd = read(fd, buf, bufsz);
+// numrd = fread(buf, bufsz, 1, f);
+ numrd = fread(buf, 1, bufsz, f);
+ i++;
+ if (numrd > 0)
+ bcnt = numrd;
+ while ((numrd > 0) && (bufsz > 0)) {
+ bufsz = time_copy_bufsz(bufsz, bcnt, maxlen);
+// numrd = read(fd, buf, bufsz);
+// numrd = fread(buf, bufsz, 1, f);
+ numrd = fread(buf, 1, bufsz, f);
+ i++;
+ if (numrd >= 0)
+// bcnt = bcnt + numrd;
+ bcnt += numrd;
+ }
+ ec = times(&tm);
+// close(fd);
+ fclose(f);
+ if (do_verbose)
+ printf("File '%s' closed\n", fn);
+ if (numrd < 0) {
+ switch (errno) {
+ case EIO :
+ printf("IO Error at %zu B reading file '%s'\n", bcnt, fn);
+ break;
+ case EINVAL :
+ printf("Invalid Mode at %zu B reading file '%s'\n", bcnt, fn);
+ break;
+ default :
+ printf("Error '%d' at %zu B reading file '%s'\n", errno, bcnt, fn);
+ }
+ rv = 2;
+ }
+ if (bcnt > 0) {
+ if (bufsz == 0)
+ printf("maxed out on maxln\n");
+ if (do_simple)
+ print_cp_result_simple(fn, bcnt, bc, ec, ibufsz, do_verbose);
+ else
+ print_cp_result_long(fn, bcnt, bc, ec, ibufsz, do_verbose);
+ }
+ if (do_verbose)
+ printf(" numrd %d bcnt %d bufsz %d i %d\n", numrd, bcnt, bufsz, i);
+ }
+ return rv;
+} /* int time_copy(char *fn, char do_simple, int bufsz, off_t maxlen) */
+
+int main(int argc, char *argv[])
+{
+ int i;
+ char do_simple = 0, do_pbuf = 0, do_plen = 0, do_verbose = 0;
+ char *arg;
+ size_t tbufsz, bufsz = min((BUFSZ_DEF), (BUFSZ_MAX));
+ off_t tmaxlen, maxlen = LEN_MAX;
+ int numfl = 0;
+ console_ansi_std();
+// openconsole(&dev_stdcon_r, &dev_stdcon_w);
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ arg = argv[i] + 1;
+ if (strcmp(arg, "b") == 0) {
+ i++;
+ if (i < argc) {
+ tbufsz = atoi(argv[i]);
+ if (tbufsz > 0)
+ bufsz = min(max((BUFSZ_MIN), tbufsz), (BUFSZ_MAX));
+ do_pbuf = 1;
+ }
+ } else if (strcmp(arg, "n") == 0) {
+ i++;
+ if (i < argc) {
+ tmaxlen = atoi(argv[i]);
+ if (tmaxlen > 0)
+ maxlen = min(max((LEN_MIN), tmaxlen), (LEN_MAX));
+ do_plen = 1;
+ }
+ } else if (strcmp(arg, "s") == 0)
+ do_simple = 1;
+ else if (strcmp(arg, "l") == 0)
+ do_simple = 0;
+ else if (strcmp(arg, "v") == 0)
+ do_verbose = 1;
+ else if (strcmp(arg, "q") == 0)
+ do_verbose = 0;
+ }
+ }
+ if (do_pbuf || do_verbose)
+ printf("Using bufsz %zu\n", bufsz);
+ if (do_plen || do_verbose)
+ printf("Using maxlen %zu\n", maxlen);
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ arg = argv[i] + 1;
+ if ((strcmp(arg, "b") == 0) || (strcmp(arg, "n") == 0))
+ i++; /* Skip next arg */
+ else if (!((strcmp(arg, "s") == 0) || (strcmp(arg, "l") == 0) || (strcmp(arg, "v") == 0) || (strcmp(arg, "q") == 0))) {
+ time_copy(argv[i], do_simple, do_verbose, bufsz, maxlen);
+ numfl++;
+ }
+ } else {
+ time_copy(argv[i], do_simple, do_verbose, bufsz, maxlen);
+ numfl++;
+ }
+ }
+ if (numfl == 0)
+ fprintf(stderr, "%s: Please specify a file\n", argv[0]);
+ return 0;
+} /* int main(int argc, char *argv[]) */
diff --git a/com32/modules/cpuid.c b/com32/modules/cpuid.c
index 78cb3f58..a2438960 100644
--- a/com32/modules/cpuid.c
+++ b/com32/modules/cpuid.c
@@ -34,8 +34,6 @@ int main(int argc, char *argv[])
uint32_t leaf, counter;
uint32_t eax, ebx, ecx, edx;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc < 2 || argc > 4) {
printf("Usage: %s leaf [counter]\n", argv[0]);
exit(1);
diff --git a/com32/modules/cpuidtest.c b/com32/modules/cpuidtest.c
index b7688852..d00256fa 100644
--- a/com32/modules/cpuidtest.c
+++ b/com32/modules/cpuidtest.c
@@ -42,7 +42,6 @@ char display_line;
int main(void)
{
s_cpu cpu;
- openconsole(&dev_stdcon_r, &dev_stdcon_w);
for (;;) {
detect_cpu(&cpu);
diff --git a/com32/modules/debug.c b/com32/modules/debug.c
new file mode 100644
index 00000000..1026ebf3
--- /dev/null
+++ b/com32/modules/debug.c
@@ -0,0 +1,54 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2013 Intel Corporation; author: Matt Fleming
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <syslinux/debug.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *progname;
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: %s [-e|-d] <func1> [<func2>, ...]\n", progname);
+}
+
+int main(int argc, char *argv[])
+{
+ bool enable;
+ int i;
+
+ progname = argv[0];
+
+ if (argc < 3) {
+ usage();
+ return -1;
+ }
+
+ if (!strncmp(argv[1], "-e", 2))
+ enable = true;
+ else if (!strncmp(argv[1], "-d", 2))
+ enable = false;
+ else {
+ usage();
+ return -1;
+ }
+
+ for (i = 2; i < argc; i++) {
+ char *str = argv[i];
+
+ if (syslinux_debug(str, enable) < 0)
+ fprintf(stderr, "Failed to debug symbol \"%s\"\n", str);
+ }
+
+ return 0;
+}
diff --git a/com32/modules/dir.c b/com32/modules/dir.c
new file mode 100644
index 00000000..01a99ed5
--- /dev/null
+++ b/com32/modules/dir.c
@@ -0,0 +1,175 @@
+/*
+ * Display directory contents
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <console.h>
+#include <string.h>
+#include <com32.h>
+#include <dirent.h>
+#include <minmax.h>
+#include <unistd.h>
+#include <getkey.h>
+
+static int rows, cols; /* Screen parameters */
+
+#define DIR_CHUNK 1024
+
+static const char *type_str(int type)
+{
+ switch (type) {
+ case DT_FIFO:
+ return "[fif]";
+ case DT_CHR:
+ return "[chr]";
+ case DT_DIR:
+ return "[dir]";
+ case DT_BLK:
+ return "[blk]";
+ case DT_UNKNOWN:
+ case DT_REG:
+ return "";
+ case DT_LNK:
+ return "[lnk]";
+ case DT_SOCK:
+ return "[sck]";
+ case DT_WHT:
+ return "[wht]";
+ default:
+ return "[???]";
+ }
+}
+
+static void free_dirents(struct dirent **dex, size_t n_de)
+{
+ size_t i;
+
+ for (i = 0; i < n_de; i++)
+ free(dex[i]);
+
+ free(dex);
+}
+
+static int compare_dirent(const void *p_de1, const void *p_de2)
+{
+ const struct dirent *de1 = *(const struct dirent **)p_de1;
+ const struct dirent *de2 = *(const struct dirent **)p_de2;
+ int ndir1, ndir2;
+
+ ndir1 = de1->d_type != DT_DIR;
+ ndir2 = de2->d_type != DT_DIR;
+
+ if (ndir1 != ndir2)
+ return ndir1 - ndir2;
+
+ return strcmp(de1->d_name, de2->d_name);
+}
+
+static int display_directory(const char *dirname)
+{
+ DIR *dir;
+ struct dirent *de;
+ struct dirent **dex = NULL;
+ size_t n_dex = 0, n_de = 0;
+ size_t i, j, k;
+ size_t nrows, ncols, perpage;
+ size_t endpage;
+ int maxlen = 0;
+ int pos, tpos, colwidth;
+
+ dir = opendir(dirname);
+ if (!dir) {
+ printf("Unable to read directory: %s\n", dirname);
+ return -1;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ struct dirent *nde;
+
+ if (n_de >= n_dex) {
+ struct dirent **ndex;
+
+ ndex = realloc(dex, (n_dex + DIR_CHUNK) * sizeof *dex);
+ if (!ndex)
+ goto nomem;
+
+ dex = ndex;
+ n_dex += DIR_CHUNK;
+ }
+
+ nde = malloc(de->d_reclen);
+ if (!nde)
+ goto nomem;
+
+ memcpy(nde, de, de->d_reclen);
+ dex[n_de++] = nde;
+
+ maxlen = max(maxlen, de->d_reclen);
+ }
+
+ closedir(dir);
+
+ qsort(dex, n_de, sizeof *dex, compare_dirent);
+
+ maxlen -= offsetof(struct dirent, d_name) + 1;
+ ncols = (cols + 2)/(maxlen + 8);
+ ncols = min(ncols, n_de);
+ ncols = max(ncols, 1U);
+ colwidth = (cols + 2)/ncols;
+ perpage = ncols * (rows - 1);
+
+ for (i = 0; i < n_de; i += perpage) {
+ /* Rows on this page */
+ endpage = min(i+perpage, n_de);
+ nrows = ((endpage-i) + ncols - 1)/ncols;
+
+ for (j = 0; j < nrows; j++) {
+ pos = tpos = 0;
+ for (k = i+j; k < endpage; k += nrows) {
+ pos += printf("%*s%-5s %s",
+ (tpos - pos), "",
+ type_str(dex[k]->d_type),
+ dex[k]->d_name);
+ tpos += colwidth;
+ }
+ printf("\n");
+ }
+
+ if (endpage >= n_de)
+ break;
+
+ get_key(stdin, 0);
+ }
+
+ free_dirents(dex, n_de);
+ return 0;
+
+nomem:
+ closedir(dir);
+ printf("Out of memory error!\n");
+ free_dirents(dex, n_de);
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int rv;
+
+ if (getscreensize(1, &rows, &cols)) {
+ /* Unknown screen size? */
+ rows = 24;
+ cols = 80;
+ }
+
+ if (argc < 2)
+ rv = display_directory(".");
+ else if (argc == 2)
+ rv = display_directory(argv[1]);
+ else {
+ printf("Usage: dir directory\n");
+ rv = 1;
+ }
+
+ return rv ? 1 : 0;
+}
+
diff --git a/com32/modules/disk.c b/com32/modules/disk.c
index ca4b5988..5d68381e 100644
--- a/com32/modules/disk.c
+++ b/com32/modules/disk.c
@@ -27,8 +27,6 @@ int main(int argc, char *argv[])
(void)argc;
(void)argv;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
for (int disk = 0x80; disk < 0xff; disk++) {
memset(d, 0, sizeof(struct driveinfo));
d->disk = disk;
diff --git a/com32/modules/dmitest.c b/com32/modules/dmitest.c
index 294585bb..4ce2eaad 100644
--- a/com32/modules/dmitest.c
+++ b/com32/modules/dmitest.c
@@ -179,7 +179,6 @@ int main(void)
{
char buffer[1024];
s_dmi dmi;
- openconsole(&dev_stdcon_r, &dev_stdcon_w);
if (dmi_iterate(&dmi) == -ENODMITABLE) {
printf("No DMI Structure found\n");
diff --git a/com32/modules/elf.c b/com32/modules/elf.c
index 0ac45174..a946af1f 100644
--- a/com32/modules/elf.c
+++ b/com32/modules/elf.c
@@ -263,8 +263,6 @@ int main(int argc, char *argv[])
void *data;
size_t data_len;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc < 2) {
error("Usage: elf.c32 elf_file arguments...\n");
return 1;
diff --git a/com32/modules/ethersel.c b/com32/modules/ethersel.c
index 28dc62ca..039de276 100644
--- a/com32/modules/ethersel.c
+++ b/com32/modules/ethersel.c
@@ -180,7 +180,6 @@ int main(int argc, char *argv[])
struct match *list, *match;
struct pci_domain *pci_domain;
- openconsole(&dev_null_r, &dev_stdcon_w);
pci_domain = pci_scan();
if (pci_domain) {
diff --git a/com32/modules/gpxecmd.c b/com32/modules/gpxecmd.c
index 057659bd..d2d90a2f 100644
--- a/com32/modules/gpxecmd.c
+++ b/com32/modules/gpxecmd.c
@@ -22,7 +22,9 @@
#include <console.h>
#include <com32.h>
#include <string.h>
+
#include <sys/gpxe.h>
+#include <syslinux/pxe_api.h>
struct segoff16 {
uint16_t offs, seg;
@@ -37,11 +39,11 @@ static void gpxecmd(const char **args)
{
char *q;
struct s_PXENV_FILE_EXEC *fx;
- com32sys_t reg;
- memset(&reg, 0, sizeof reg);
+ fx = lmalloc(sizeof *fx);
+ if (!fx)
+ return;
- fx = __com32.cs_bounce;
q = (char *)(fx + 1);
fx->Status = 1;
@@ -55,21 +57,13 @@ static void gpxecmd(const char **args)
}
*--q = '\0';
- memset(&reg, 0, sizeof reg);
- reg.eax.w[0] = 0x0009;
- reg.ebx.w[0] = 0x00e5; /* PXENV_FILE_EXEC */
- reg.edi.w[0] = OFFS(fx);
- reg.es = SEG(fx);
-
- __intcall(0x22, &reg, &reg);
+ pxe_call(PXENV_FILE_EXEC, fx);
/* This should not return... */
}
int main(int argc, const char *argv[])
{
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc < 2) {
printf("Usage: gpxecmd command...\n");
return 1;
diff --git a/com32/modules/hexdump.c b/com32/modules/hexdump.c
new file mode 100644
index 00000000..bc2c70dd
--- /dev/null
+++ b/com32/modules/hexdump.c
@@ -0,0 +1,245 @@
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <console.h>
+#include <errno.h>
+#include <syslinux/loadfile.h>
+
+/* Macros */
+#define ROWS_PER_PAGE 24
+#define COLS_PER_ROW 16
+#define BYTES_PER_PAGE (ROWS_PER_PAGE * COLS_PER_ROW)
+
+/* Functions declarations */
+static int usage(void);
+static void eat_stdin(void);
+static int do_page(void);
+static void hexdump(const void *memory, size_t bytes);
+
+/* Objects */
+static const char *prog_name;
+static int opt_page;
+static int opt_no_buffer;
+static int opt_extended_ascii;
+
+int main(int argc, char **argv)
+{
+ int rc;
+ const char *filename;
+ int i;
+ void *file_data;
+ size_t file_sz;
+ FILE *f;
+ size_t len;
+ const char *cur_pos;
+
+ /* Assume failure */
+ rc = EXIT_FAILURE;
+
+ /* Determine the program name, as invoked */
+ if (argc < 1 || !argv || !argv[0]) {
+ fprintf(stderr, "argc or argv failure!\n");
+ goto err_prog_name;
+ }
+ prog_name = argv[0];
+
+ /* Process arguments */
+ filename = NULL;
+ for (i = 1; i < argc; ++i) {
+ if (!argv[i]) {
+ fprintf(stderr, "argc and argv mismatch!\n");
+ goto err_argv;
+ }
+
+ if (!strncmp(argv[i], "--page", sizeof "--page") ||
+ !strncmp(argv[i], "-p", sizeof "-p")) {
+ opt_page = 1;
+ continue;
+ }
+
+ if (!strncmp(argv[i], "--no-buffer", sizeof "--no-buffer")) {
+ opt_no_buffer = 1;
+ continue;
+ }
+
+ if (!strncmp(argv[i], "--extended-ascii", sizeof "--extended-ascii")) {
+ opt_extended_ascii = 1;
+ continue;
+ }
+
+ if (!strncmp(argv[i], "--help", sizeof "--help") ||
+ !strncmp(argv[i], "-h", sizeof "-h") ||
+ !strncmp(argv[i], "-?", sizeof "-?"))
+ return usage();
+
+ /* Otherwise, interpret as a filename, but only accept one */
+ if (filename)
+ return usage();
+ filename = argv[i];
+ }
+ if (!filename)
+ return usage();
+ fprintf(stdout, "Dumping file: %s\n", filename);
+
+ /* Either fetch the whole file, or just allocate a buffer */
+ f = NULL;
+ if (opt_no_buffer) {
+ errno = 0;
+ if (loadfile(filename, &file_data, &file_sz)) {
+ fprintf(stderr, "Couldn't load file. Error: %d\n", errno);
+ goto err_file_data;
+ }
+ } else {
+ file_sz = BYTES_PER_PAGE;
+ file_data = malloc(file_sz);
+ if (!file_data) {
+ fprintf(stderr, "Couldn't allocate file data buffer\n");
+ goto err_file_data;
+ }
+ errno = 0;
+ f = fopen(filename, "r");
+ if (!f) {
+ fprintf(stderr, "Couldn't open file. Error: %d\n", errno);
+ goto err_f;
+ }
+ }
+
+ /* Dump the data */
+ len = BYTES_PER_PAGE;
+ cur_pos = file_data;
+ do {
+ if (f) {
+ /* Buffered */
+ len = fread(file_data, 1, file_sz, f);
+ cur_pos = file_data;
+ } else {
+ /* Non-buffered */
+ if (file_sz < len)
+ len = file_sz;
+ }
+ if (!len)
+ break;
+
+ hexdump(cur_pos, len);
+
+ /* Pause, if requested */
+ if (opt_page) {
+ /* The user might choose to quit */
+ if (do_page())
+ break;
+ }
+
+ /* Reduce file_sz for non-buffered mode */
+ if (!f)
+ file_sz -= len;
+ } while (cur_pos += len);
+
+ rc = EXIT_SUCCESS;
+
+ if (f)
+ fclose(f);
+ err_f:
+
+ free(file_data);
+ err_file_data:
+
+ err_argv:
+
+ err_prog_name:
+
+ return rc;
+}
+
+static int usage(void)
+{
+ static const char usage[] =
+ "Usage: %s [<option> [...]] <filename> [<option> [...]]\n"
+ "\n"
+ "Options: -p\n"
+ " --page . . . . . . . Pause output every 24 lines\n"
+ " --no-buffer . . . . Load the entire file before dumping\n"
+ " --extended-ascii . . Use extended ASCII chars in dump\n"
+ " -?\n"
+ " -h\n"
+ " --help . . . . . . Display this help\n";
+
+ fprintf(stderr, usage, prog_name);
+ return EXIT_FAILURE;
+}
+
+static void eat_stdin(void)
+{
+ int i;
+
+ while (1) {
+ i = fgetc(stdin);
+ if (i == EOF || i == '\n')
+ return;
+ }
+}
+static int do_page(void)
+{
+ int i;
+
+ while (1) {
+ fprintf(stdout, "Continue? [Y|n]: ");
+ i = fgetc(stdin);
+ switch (i) {
+ case 'n':
+ case 'N':
+ eat_stdin();
+ return 1;
+
+ case EOF:
+ fprintf(stderr, "No response. Continuing...\n");
+ /* Fall through to "yes" */
+
+ case 'y':
+ case 'Y':
+ eat_stdin();
+ case '\n':
+ return 0;
+
+ default:
+ fprintf(stderr, "Invalid choice\n");
+ eat_stdin();
+ }
+ }
+}
+
+static void hexdump(const void *memory, size_t bytes)
+{
+ const unsigned char *p, *q;
+ int i;
+
+ p = memory;
+ while (bytes) {
+ q = p;
+ printf("%p: ", (void *) p);
+ for (i = 0; i < 16 && bytes; ++i) {
+ printf("%02X ", *p);
+ ++p;
+ --bytes;
+ }
+ bytes += i;
+ while (i < 16) {
+ printf("XX ");
+ ++i;
+ }
+ printf("| ");
+ p = q;
+ for (i = 0; i < 16 && bytes; ++i) {
+ printf("%c", isprint(*p) && !isspace(*p) ? *p : ' ');
+ ++p;
+ --bytes;
+ }
+ while (i < 16) {
+ printf(" ");
+ ++i;
+ }
+ printf("\n");
+ }
+ return;
+}
diff --git a/com32/modules/host.c b/com32/modules/host.c
index 94ca876d..d70efffd 100644
--- a/com32/modules/host.c
+++ b/com32/modules/host.c
@@ -1,41 +1,41 @@
#include <stdio.h>
-#include <console.h>
+#include <stdlib.h>
#include <string.h>
+#include <console.h>
#include <netinet/in.h>
#include <com32.h>
+#include <syslinux/pxe.h>
-static struct in_addr dnsresolve(const char *hostname)
+static inline uint32_t dns_resolve(const char *hostname)
{
- com32sys_t regs;
- struct in_addr addr;
-
- strcpy(__com32.cs_bounce, hostname);
-
- regs.eax.w[0] = 0x0010;
- regs.es = SEG(__com32.cs_bounce);
- regs.ebx.w[0] = OFFS(__com32.cs_bounce);
- __intcall(0x22, &regs, &regs);
+ return pxe_dns(hostname);
+}
- addr.s_addr = regs.eax.l;
- return addr;
+static inline void usage(const char *s)
+{
+ fprintf(stderr, "Usage: %s hostname [, hostname_1, hostname_2, ...]\n", s);
}
int main(int argc, char *argv[])
{
int i;
- struct in_addr addr;
+ uint32_t ip;
openconsole(&dev_null_r, &dev_stdcon_w);
- for (i = 1; i < argc; i++) {
- addr = dnsresolve(argv[i]);
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
- printf("%-39s %08X %d.%d.%d.%d\n",
- argv[i], ntohl(addr.s_addr),
- ((uint8_t *)&addr.s_addr)[0],
- ((uint8_t *)&addr.s_addr)[1],
- ((uint8_t *)&addr.s_addr)[2],
- ((uint8_t *)&addr.s_addr)[3]);
+ for (i = 1; i < argc; i++) {
+ ip = dns_resolve(argv[i]);
+ if (!ip) {
+ printf("%s not found.\n", argv[i]);
+ } else {
+ printf("%-39s %08X %u.%u.%u.%u\n", argv[i], ntohl(ip), ip & 0xFF,
+ (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
+ }
}
return 0;
diff --git a/com32/modules/ifplop.c b/com32/modules/ifplop.c
index a846df8d..f502e2b3 100644
--- a/com32/modules/ifplop.c
+++ b/com32/modules/ifplop.c
@@ -144,8 +144,6 @@ int main(int argc, char *argv[])
char **args[2];
int arg = 0;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc)
arg++;
args[0] = &argv[arg];
diff --git a/com32/modules/kbdmap.c b/com32/modules/kbdmap.c
index f1c736d5..7ec40018 100644
--- a/com32/modules/kbdmap.c
+++ b/com32/modules/kbdmap.c
@@ -27,8 +27,6 @@ int main(int argc, char *argv[])
size_t map_size;
void *kbdmap;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc != 2) {
error("Usage: kbdmap mapfile\n");
return 1;
diff --git a/com32/modules/linux.c b/com32/modules/linux.c
index 76443f91..f657eab4 100644
--- a/com32/modules/linux.c
+++ b/com32/modules/linux.c
@@ -48,6 +48,14 @@
#include <syslinux/linux.h>
#include <syslinux/pxe.h>
+enum ldmode {
+ ldmode_raw,
+ ldmode_cpio,
+ ldmodes
+};
+
+typedef int f_ldinitramfs(struct initramfs *, char *);
+
const char *progname = "linux.c32";
/* Find the last instance of a particular command line argument
@@ -59,13 +67,34 @@ static char *find_argument(char **argv, const char *argument)
char *ptr = NULL;
for (arg = argv; *arg; arg++) {
- if (!memcmp(*arg, argument, la))
+ if (!strncmp(*arg, argument, la))
ptr = *arg + la;
}
return ptr;
}
+/* Find the next instance of a particular command line argument */
+static char **find_arguments(char **argv, char **ptr,
+ const char *argument)
+{
+ int la = strlen(argument);
+ char **arg;
+
+ for (arg = argv; *arg; arg++) {
+ if (!strncmp(*arg, argument, la)) {
+ *ptr = *arg + la;
+ break;
+ }
+ }
+
+ /* Exhausted all arguments */
+ if (!*arg)
+ return NULL;
+
+ return arg;
+}
+
/* Search for a boolean argument; return its position, or 0 if not present */
static int find_boolean(char **argv, const char *argument)
{
@@ -109,6 +138,99 @@ static char *make_cmdline(char **argv)
return cmdline;
}
+static f_ldinitramfs ldinitramfs_raw;
+static int ldinitramfs_raw(struct initramfs *initramfs, char *fname)
+{
+ return initramfs_load_archive(initramfs, fname);
+}
+
+static f_ldinitramfs ldinitramfs_cpio;
+static int ldinitramfs_cpio(struct initramfs *initramfs, char *fname)
+{
+ char *target_fname, *p;
+ int do_mkdir, unmangle, rc;
+
+ /* Choose target_fname based on presence of "@" syntax */
+ target_fname = strchr(fname, '@');
+ if (target_fname) {
+ /* Temporarily mangle */
+ unmangle = 1;
+ *target_fname++ = '\0';
+
+ /* Make parent directories? */
+ do_mkdir = !!strchr(target_fname, '/');
+ } else {
+ unmangle = 0;
+
+ /* Forget the source path */
+ target_fname = fname;
+ while ((p = strchr(target_fname, '/')))
+ target_fname = p + 1;
+
+ /* The user didn't specify a desired path */
+ do_mkdir = 0;
+ }
+
+ /*
+ * Load the file, encapsulate it with the desired path, make the
+ * parent directories if the desired path contains them, add to initramfs
+ */
+ rc = initramfs_load_file(initramfs, fname, target_fname, do_mkdir, 0755);
+
+ /* Unmangle, if needed*/
+ if (unmangle)
+ *--target_fname = '@';
+
+ return rc;
+}
+
+/* It only makes sense to call this function from main */
+static int process_initramfs_args(char *arg, struct initramfs *initramfs,
+ const char *kernel_name, enum ldmode mode,
+ bool opt_quiet)
+{
+ const char *mode_msg;
+ f_ldinitramfs *ldinitramfs;
+ char *p;
+
+ switch (mode) {
+ case ldmode_raw:
+ mode_msg = "Loading";
+ ldinitramfs = ldinitramfs_raw;
+ break;
+ case ldmode_cpio:
+ mode_msg = "Encapsulating";
+ ldinitramfs = ldinitramfs_cpio;
+ break;
+ case ldmodes:
+ default:
+ return 1;
+ }
+
+ do {
+ p = strchr(arg, ',');
+ if (p)
+ *p = '\0';
+
+ if (!opt_quiet)
+ printf("%s %s... ", mode_msg, arg);
+ errno = 0;
+ if (ldinitramfs(initramfs, arg)) {
+ if (opt_quiet)
+ printf("Loading %s ", kernel_name);
+ printf("failed: ");
+ return 1;
+ }
+ if (!opt_quiet)
+ printf("ok\n");
+
+ if (p)
+ *p++ = ',';
+ } while ((arg = p));
+
+ return 0;
+}
+
static int setup_data_file(struct setup_data *setup_data,
uint32_t type, const char *filename,
bool opt_quiet)
@@ -142,9 +264,7 @@ int main(int argc, char *argv[])
bool opt_quiet = false;
void *dhcpdata;
size_t dhcplen;
- char **argp, **argl, *arg, *p;
-
- openconsole(&dev_null_r, &dev_stdcon_w);
+ char **argp, **argl, *arg;
(void)argc;
argp = argv + 1;
@@ -209,27 +329,27 @@ int main(int argc, char *argv[])
goto bail;
}
+ /* Process initramfs arguments */
if ((arg = find_argument(argp, "initrd="))) {
- do {
- p = strchr(arg, ',');
- if (p)
- *p = '\0';
-
- if (!opt_quiet)
- printf("Loading %s... ", arg);
- errno = 0;
- if (initramfs_load_archive(initramfs, arg)) {
- if (opt_quiet)
- printf("Loading %s ", kernel_name);
- printf("failed: ");
- goto bail;
- }
- if (!opt_quiet)
- printf("ok\n");
-
- if (p)
- *p++ = ',';
- } while ((arg = p));
+ if (process_initramfs_args(arg, initramfs, kernel_name, ldmode_raw,
+ opt_quiet))
+ goto bail;
+ }
+
+ argl = argv;
+ while ((argl = find_arguments(argl, &arg, "initrd+="))) {
+ argl++;
+ if (process_initramfs_args(arg, initramfs, kernel_name, ldmode_raw,
+ opt_quiet))
+ goto bail;
+ }
+
+ argl = argv;
+ while ((argl = find_arguments(argl, &arg, "initrdfile="))) {
+ argl++;
+ if (process_initramfs_args(arg, initramfs, kernel_name, ldmode_cpio,
+ opt_quiet))
+ goto bail;
}
/* Append the DHCP info */
@@ -248,24 +368,28 @@ int main(int argc, char *argv[])
if (!setup_data)
goto bail;
- for (argl = argv; (arg = *argl); argl++) {
- if (!memcmp(arg, "dtb=", 4)) {
- if (setup_data_file(setup_data, SETUP_DTB, arg+4, opt_quiet))
- goto bail;
- } else if (!memcmp(arg, "blob.", 5)) {
- uint32_t type;
- char *ep;
+ argl = argv;
+ while ((argl = find_arguments(argl, &arg, "dtb="))) {
+ argl++;
+ if (setup_data_file(setup_data, SETUP_DTB, arg, opt_quiet))
+ goto bail;
+ }
+
+ argl = argv;
+ while ((argl = find_arguments(argl, &arg, "blob."))) {
+ uint32_t type;
+ char *ep;
- type = strtoul(arg + 5, &ep, 10);
- if (ep[0] != '=' || !ep[1])
- continue;
+ argl++;
+ type = strtoul(arg, &ep, 10);
+ if (ep[0] != '=' || !ep[1])
+ continue;
- if (!type)
- continue;
+ if (!type)
+ continue;
- if (setup_data_file(setup_data, type, ep+1, opt_quiet))
- goto bail;
- }
+ if (setup_data_file(setup_data, type, ep+1, opt_quiet))
+ goto bail;
}
/* This should not return... */
diff --git a/com32/modules/ls.c b/com32/modules/ls.c
index c311621e..11c18ae0 100644
--- a/com32/modules/ls.c
+++ b/com32/modules/ls.c
@@ -155,8 +155,6 @@ int main(int argc, char *argv[])
{
int rv;
- openconsole(&dev_rawcon_r, &dev_stdcon_w);
-
if (getscreensize(1, &rows, &cols)) {
/* Unknown screen size? */
rows = 24;
diff --git a/com32/modules/meminfo.c b/com32/modules/meminfo.c
index 6e24f355..34b3e91d 100644
--- a/com32/modules/meminfo.c
+++ b/com32/modules/meminfo.c
@@ -42,27 +42,32 @@ static void dump_e820(void)
com32sys_t ireg, oreg;
struct e820_data ed;
uint32_t type;
+ void *low_ed;
+
+ low_ed = lmalloc(sizeof ed);
+ if (!low_ed)
+ return;
memset(&ireg, 0, sizeof ireg);
ireg.eax.w[0] = 0xe820;
ireg.edx.l = 0x534d4150;
ireg.ecx.l = sizeof(struct e820_data);
- ireg.edi.w[0] = OFFS(__com32.cs_bounce);
- ireg.es = SEG(__com32.cs_bounce);
+ ireg.edi.w[0] = OFFS(low_ed);
+ ireg.es = SEG(low_ed);
memset(&ed, 0, sizeof ed);
ed.extattr = 1;
do {
- memcpy(__com32.cs_bounce, &ed, sizeof ed);
+ memcpy(low_ed, &ed, sizeof ed);
__intcall(0x15, &ireg, &oreg);
if (oreg.eflags.l & EFLAGS_CF ||
oreg.eax.l != 0x534d4150 || oreg.ecx.l < 20)
break;
- memcpy(&ed, __com32.cs_bounce, sizeof ed);
+ memcpy(&ed, low_ed, sizeof ed);
if (oreg.ecx.l >= 24) {
/* ebx base length end type */
@@ -84,6 +89,8 @@ static void dump_e820(void)
ireg.ebx.l = oreg.ebx.l;
} while (ireg.ebx.l);
+
+ lfree(low_ed);
}
static void dump_legacy(void)
@@ -115,12 +122,9 @@ static void dump_legacy(void)
oreg.ecx.w[0], oreg.ecx.w[0], oreg.edx.w[0], oreg.edx.w[0] << 6);
}
-int main(void)
+int main(int argc __unused, char **argv __unused)
{
- openconsole(&dev_null_r, &dev_stdcon_w);
-
dump_legacy();
dump_e820();
-
return 0;
}
diff --git a/com32/modules/pcitest.c b/com32/modules/pcitest.c
index 9921ee6d..fb6bbbf1 100644
--- a/com32/modules/pcitest.c
+++ b/com32/modules/pcitest.c
@@ -103,8 +103,6 @@ int main(int argc, char *argv[])
(void)argc;
(void)argv;
- openconsole(&dev_stdcon_r, &dev_stdcon_w);
-
/* Scanning to detect pci buses and devices */
printf("PCI: Scanning PCI BUS\n");
pci_domain = pci_scan();
diff --git a/com32/modules/pmload.c b/com32/modules/pmload.c
index 4c01db08..6808d38a 100644
--- a/com32/modules/pmload.c
+++ b/com32/modules/pmload.c
@@ -198,8 +198,6 @@ int main(int argc, char *argv[])
size_t data_len;
addr_t where;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc < 3) {
error("Usage: pmload.c32 bin_file address arguments...\n");
return 1;
diff --git a/com32/modules/poweroff.c b/com32/modules/poweroff.c
new file mode 100644
index 00000000..8b656ad4
--- /dev/null
+++ b/com32/modules/poweroff.c
@@ -0,0 +1,88 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2013 Sebastian Herbszt - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * poweroff.c
+ *
+ * APM poweroff module
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <com32.h>
+
+int main()
+{
+ com32sys_t inregs, outregs;
+
+ memset(&inregs, 0, sizeof inregs);
+
+ inregs.eax.l = 0x5300; /* APM Installation Check (00h) */
+ inregs.ebx.l = 0; /* APM BIOS (0000h) */
+ __intcall(0x15, &inregs, &outregs);
+
+ if (outregs.eflags.l & EFLAGS_CF) {
+ printf("APM not present.\n");
+ return 1;
+ }
+
+ if ((outregs.ebx.l & 0xffff) != 0x504d) { /* signature 'PM' */
+ printf("APM not present.\n");
+ return 1;
+ }
+
+ if ((outregs.eax.l & 0xffff) < 0x101) { /* Need version 1.1+ */
+ printf("APM 1.1+ not supported.\n");
+ return 1;
+ }
+
+ if ((outregs.ecx.l & 0x8) == 0x8) { /* bit 3 APM BIOS Power Management disabled */
+ printf("Power management disabled.\n");
+ return 1;
+ }
+
+ inregs.eax.l = 0x5301; /* APM Real Mode Interface Connect (01h) */
+ inregs.ebx.l = 0; /* APM BIOS (0000h) */
+ __intcall(0x15, &inregs, &outregs);
+
+ if (outregs.eflags.l & EFLAGS_CF) {
+ printf("APM RM interface connect failed.\n");
+ return 1;
+ }
+
+ inregs.eax.l = 0x530e; /* APM Driver Version (0Eh) */
+ inregs.ebx.l = 0; /* APM BIOS (0000h) */
+ inregs.ecx.l = 0x101; /* APM Driver version 1.1 */
+ __intcall(0x15, &inregs, &outregs);
+
+ if (outregs.eflags.l & EFLAGS_CF) {
+ printf("APM 1.1+ not supported.\n");
+ return 1;
+ }
+
+ if ((outregs.ecx.l & 0xffff) < 0x101) { /* APM Connection version */
+ printf("APM 1.1+ not supported.\n");
+ return 1;
+ }
+
+ inregs.eax.l = 0x5307; /* Set Power State (07h) */
+ inregs.ebx.l = 1; /* All devices power managed by the APM BIOS */
+ inregs.ecx.l = 3; /* Power state off */
+ __intcall(0x15, &inregs, &outregs);
+
+ if (outregs.eflags.l & EFLAGS_CF) {
+ printf("Power off failed.\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/com32/modules/prdhcp.c b/com32/modules/prdhcp.c
index e1785a03..4ae295e8 100644
--- a/com32/modules/prdhcp.c
+++ b/com32/modules/prdhcp.c
@@ -39,11 +39,11 @@
#include <unistd.h>
#include <getkey.h>
-#define DEBUG 0
+#define PRDHCP_DEBUG 0
#define dprintf0(f, ...) ((void)0)
-#ifdef DEBUG
+#ifdef PRDHCP_DEBUG
# define dpressanykey pressanykey
# define dprintf printf
# define dprint_pxe_bootp_t print_pxe_bootp_t
diff --git a/com32/modules/pwd.c b/com32/modules/pwd.c
index 880327d6..2794e3dd 100644
--- a/com32/modules/pwd.c
+++ b/com32/modules/pwd.c
@@ -36,7 +36,6 @@ int main(void)
int rv = 0;
char pwd[PATH_MAX], *pwdptr;
- openconsole(&dev_rawcon_r, &dev_stdcon_w);
pwdptr = getcwd(pwd, PATH_MAX);
if (pwdptr) {
if (pwd[0] != 0)
diff --git a/com32/modules/pxechn.c b/com32/modules/pxechn.c
index 26376900..7f2002db 100644
--- a/com32/modules/pxechn.c
+++ b/com32/modules/pxechn.c
@@ -884,7 +884,6 @@ int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe)
*/
int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype)
{
- com32sys_t reg;
t_PXENV_GET_CACHED_INFO *ci;
void *cp;
int rv = -1;
@@ -896,12 +895,7 @@ int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype)
}
ci->Status = PXENV_STATUS_FAILURE;
ci->PacketType = ptype;
- memset(&reg, 0, sizeof(reg));
- reg.eax.w[0] = 0x0009;
- reg.ebx.w[0] = PXENV_GET_CACHED_INFO;
- reg.edi.w[0] = OFFS(ci);
- reg.es = SEG(ci);
- __intcall(0x22, &reg, &reg);
+ pxe_call(PXENV_GET_CACHED_INFO, ci);
if (ci->Status != PXENV_STATUS_SUCCESS) {
dprintf("PXE Get Cached Info failed: %d\n", ci->Status);
@@ -1054,7 +1048,6 @@ int pxe_restart(char *ifn)
{
int rv = 0;
struct pxelinux_opt pxe;
- com32sys_t reg;
t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */
pxe.fn = ifn;
@@ -1069,11 +1062,7 @@ int pxe_restart(char *ifn)
goto ret;
}
printf(" Attempting to boot '%s'...\n\n", pxe.fn);
- memset(&reg, 0, sizeof reg);
- if (sizeof(t_PXENV_TFTP_READ_FILE) <= __com32.cs_bounce_size) {
- pxep = __com32.cs_bounce;
- memset(pxep, 0, sizeof(t_PXENV_RESTART_TFTP));
- } else if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){
+ if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){
dprintf("Unable to lzalloc() for PXE call structure\n");
goto ret;
}
@@ -1086,16 +1075,11 @@ int pxe_restart(char *ifn)
pxep->ServerIPAddress, (unsigned int)pxep,
pxep->BufferSize, (unsigned int)pxep->Buffer);
dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status);
- reg.eax.w[0] = 0x0009;
- reg.ebx.w[0] = PXENV_RESTART_TFTP;
- reg.edi.w[0] = OFFS(pxep);
- reg.es = SEG(pxep);
- __intcall(0x22, &reg, &reg);
+ pxe_call(PXENV_RESTART_TFTP, pxep);
printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status);
- if (pxep != __com32.cs_bounce)
- lfree(pxep);
+ lfree(pxep);
ret:
return rv;
diff --git a/com32/modules/sanboot.c b/com32/modules/sanboot.c
index 46df6bc3..ff55f68e 100644
--- a/com32/modules/sanboot.c
+++ b/com32/modules/sanboot.c
@@ -22,7 +22,9 @@
#include <console.h>
#include <com32.h>
#include <string.h>
+
#include <sys/gpxe.h>
+#include <syslinux/pxe_api.h>
struct segoff16 {
uint16_t offs, seg;
@@ -37,11 +39,11 @@ static void sanboot(const char **args)
{
char *q;
struct s_PXENV_FILE_EXEC *fx;
- com32sys_t reg;
- memset(&reg, 0, sizeof reg);
+ fx = lmalloc(sizeof *fx);
+ if (!fx)
+ return;
- fx = __com32.cs_bounce;
q = (char *)(fx + 1);
fx->Status = 1;
@@ -56,21 +58,13 @@ static void sanboot(const char **args)
args++;
}
- memset(&reg, 0, sizeof reg);
- reg.eax.w[0] = 0x0009;
- reg.ebx.w[0] = 0x00e5; /* PXENV_FILE_EXEC */
- reg.edi.w[0] = OFFS(fx);
- reg.es = SEG(fx);
-
- __intcall(0x22, &reg, &reg);
+ pxe_call(PXENV_FILE_EXEC, fx);
/* This should not return... */
}
int main(int argc, const char *argv[])
{
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc < 2) {
printf("Usage: sanboot rootpath\n");
return 1;
diff --git a/com32/modules/sdi.c b/com32/modules/sdi.c
index 69841d20..e57ad4a3 100644
--- a/com32/modules/sdi.c
+++ b/com32/modules/sdi.c
@@ -160,8 +160,6 @@ int main(int argc, char *argv[])
void *data;
size_t data_len;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
if (argc != 2) {
error("Usage: sdi.c32 sdi_file\n");
return 1;
diff --git a/com32/modules/vesainfo.c b/com32/modules/vesainfo.c
index 00181e47..66b121d7 100644
--- a/com32/modules/vesainfo.c
+++ b/com32/modules/vesainfo.c
@@ -20,25 +20,31 @@ static void wait_key(void)
static void print_modes(void)
{
- static com32sys_t rm;
- struct vesa_general_info *gi;
- struct vesa_mode_info *mi;
- uint16_t mode, *mode_ptr;
- int lines;
-
- /* Allocate space in the bounce buffer for these structures */
- gi = &((struct vesa_info *)__com32.cs_bounce)->gi;
- mi = &((struct vesa_info *)__com32.cs_bounce)->mi;
-
- gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */
- rm.eax.w[0] = 0x4F00; /* Get SVGA general information */
- rm.edi.w[0] = OFFS(gi);
- rm.es = SEG(gi);
- __intcall(0x10, &rm, &rm);
+ static com32sys_t rm;
+ struct vesa_general_info *gi;
+ struct vesa_mode_info *mi;
+ uint16_t mode, *mode_ptr;
+ int lines;
+
+ struct vesa_info *vesa;
+
+ vesa = lmalloc(sizeof(*vesa));
+ if (!vesa) {
+ printf("vesainfo.c32: fail in lmalloc\n");
+ return;
+ }
+ gi = &vesa->gi;
+ mi = &vesa->mi;
+
+ gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */
+ rm.eax.w[0] = 0x4F00; /* Get SVGA general information */
+ rm.edi.w[0] = OFFS(gi);
+ rm.es = SEG(gi);
+ __intcall(0x10, &rm, &rm);
if (rm.eax.w[0] != 0x004F) {
printf("No VESA BIOS detected\n");
- return;
+ goto exit;
} else if (gi->signature != VESA_MAGIC) {
printf("VESA information structure has bad magic, trying anyway...\n");
}
@@ -71,12 +77,14 @@ static void print_modes(void)
mode, mi->mode_attr, mi->h_res, mi->v_res, mi->bpp,
mi->memory_layout, mi->rpos, mi->gpos, mi->bpos);
}
+
+exit:
+ lfree(vesa);
+ return;
}
-int main(void)
+int main(int argc __unused, char **argv __unused)
{
- openconsole(&dev_rawcon_r, &dev_stdcon_w);
-
print_modes();
return 0;
}
diff --git a/com32/modules/vpdtest.c b/com32/modules/vpdtest.c
index 1d50c680..c0f32ba6 100644
--- a/com32/modules/vpdtest.c
+++ b/com32/modules/vpdtest.c
@@ -40,7 +40,6 @@
int main(void)
{
s_vpd vpd;
- openconsole(&dev_stdcon_r, &dev_stdcon_w);
if (vpd_decode(&vpd) == -ENOVPDTABLE) {
printf("No VPD Structure found\n");
diff --git a/com32/modules/whichsys.c b/com32/modules/whichsys.c
index af133f2c..777cb9e8 100644
--- a/com32/modules/whichsys.c
+++ b/com32/modules/whichsys.c
@@ -78,8 +78,6 @@ int main(int argc, char *argv[])
int arg = 0;
- openconsole(&dev_null_r, &dev_stdcon_w);
-
/* If no argument got passed, let's show the usage */
if (argc == 1) {
usage();
diff --git a/com32/rosh/Makefile b/com32/rosh/Makefile
index 766f68d5..5b54225b 100644
--- a/com32/rosh/Makefile
+++ b/com32/rosh/Makefile
@@ -16,6 +16,8 @@
## ROSH Read Only Shell
##
+LIBS = $(com32)/libutil/libutil.c32 $(com32)/lib/libcom32.c32
+
topdir = ../..
MAKEDIR = $(topdir)/mk
include $(MAKEDIR)/rosh.mk
diff --git a/com32/samples/Makefile b/com32/samples/Makefile
index 76986d59..f6ae00ab 100644
--- a/com32/samples/Makefile
+++ b/com32/samples/Makefile
@@ -14,9 +14,11 @@
## samples for syslinux users
##
+LIBS = $(com32)/libutil/libutil.c32
+
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
all: hello.c32 resolv.c32 serialinfo.c32 \
localboot.c32 \
diff --git a/com32/samples/hello.c b/com32/samples/hello.c
index 77e93ac7..d3d4d299 100644
--- a/com32/samples/hello.c
+++ b/com32/samples/hello.c
@@ -1,35 +1,26 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
/*
- * hello.c
+ * hello.c - A simple ELF module that sorts a couple of numbers
*
- * Hello, World! using libcom32
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
*/
-#include <string.h>
#include <stdio.h>
-#include <console.h>
+#include <stdlib.h>
-int main(int argc, char *argv[])
-{
- int i;
+#include "sort.h"
+
+#define NUM_COUNT 10
+#define MAX_NUM 100
- openconsole(&dev_stdcon_r, &dev_stdcon_w);
+int main(int argc __unused, char **argv __unused)
+{
+ int *nums = NULL;
- printf("Hello, World!\n");
+ nums = malloc(NUM_COUNT * sizeof(int));
+ printf("Hello, world, from 0x%08X! malloc return %p\n", (unsigned int)&main, nums);
- for (i = 1; i < argc; i++)
- printf("%s%c", argv[i], (i == argc - 1) ? '\n' : ' ');
+ free(nums);
return 0;
}
diff --git a/com32/samples/resolv.c b/com32/samples/resolv.c
index bd49d9f9..f4a0e52a 100644
--- a/com32/samples/resolv.c
+++ b/com32/samples/resolv.c
@@ -16,6 +16,7 @@
* Resolve an IP address
*/
+#include <syslinux/pxe_api.h>
#include <string.h>
#include <stdio.h>
#include <console.h>
@@ -24,21 +25,7 @@
uint32_t resolv(const char *name)
{
- com32sys_t reg;
-
- strcpy((char *)__com32.cs_bounce, name);
-
- memset(&reg, 0, sizeof reg);
- reg.eax.w[0] = 0x0010;
- reg.ebx.w[0] = OFFS(__com32.cs_bounce);
- reg.es = SEG(__com32.cs_bounce);
-
- __intcall(0x22, &reg, &reg);
-
- if (reg.eflags.l & EFLAGS_CF)
- return 0;
- else
- return reg.eax.l;
+ return dns_resolv(name);
}
int main(int argc, char *argv[])
diff --git a/com32/sysdump/Makefile b/com32/sysdump/Makefile
index 98e7f15a..7d42ae0c 100644
--- a/com32/sysdump/Makefile
+++ b/com32/sysdump/Makefile
@@ -17,7 +17,7 @@
topdir = ../..
MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
-include $(topdir)/version.mk
LIBS = ../libupload/libcom32upload.a
@@ -49,7 +49,7 @@ sysdump.elf : $(OBJS) $(LIBS) $(C_LIBS)
$(LD) $(LDFLAGS) -o $@ $^
tidy dist:
- rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
+ rm -f *.o *.lo *.a *.lst .*.d *.tmp
clean: tidy
rm -f *.lnx
diff --git a/com32/sysdump/README b/com32/sysdump/README
index 2b825775..7d10e32b 100644
--- a/com32/sysdump/README
+++ b/com32/sysdump/README
@@ -1,4 +1,4 @@
-This is a very simple COMBOOT program which can be used to dump memory
+This is a very simple COM32 program which can be used to dump memory
regions over a serial port. To use it, type on the SYSLINUX command
line:
diff --git a/com32/sysdump/main.c b/com32/sysdump/main.c
index f672585d..eac931e5 100644
--- a/com32/sysdump/main.c
+++ b/com32/sysdump/main.c
@@ -72,7 +72,6 @@ int main(int argc, char *argv[])
{
struct upload_backend **bep, *be;
- openconsole(&dev_null_r, &dev_stdcon_w);
fputs(version, stdout);
if (argc < 2)
diff --git a/core/Makefile b/core/Makefile
index 112fe3a8..f80c6d63 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -25,7 +25,8 @@ include $(MAKEDIR)/embedded.mk
-include $(topdir)/version.mk
OPTFLAGS =
-INCLUDES = -I./include -I$(com32)/include
+INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib \
+ -I./lwip/src/include -I./lwip/src/include/ipv4 -I./fs/pxe
# This is very similar to cp437; technically it's for Norway and Denmark,
# but it's unlikely the characters that are different will be used in
@@ -35,30 +36,54 @@ CODEPAGE = cp865
# The targets to build in this directory...
BTARGET = kwdhash.gen \
ldlinux.bss ldlinux.sys ldlinux.bin \
- isolinux.bin isolinux-debug.bin pxelinux.0
+ isolinux.bin isolinux-debug.bin pxelinux.0 lpxelinux.0
# All primary source files for the main syslinux files
NASMSRC := $(wildcard *.asm)
NASMHDR := $(wildcard *.inc)
-CSRC := $(wildcard *.c */*.c */*/*.c)
-SSRC := $(wildcard *.S */*.S */*/*.S)
-CHDR := $(wildcard *.h)
+CSRC := $(shell find . -name '*.c' -print)
+SSRC := $(shell find . -name '*.S' -print)
+CHDR := $(shell find . -name '*.h' -print)
OTHERSRC := keywords
ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(SSRC) $(CHDR) $(OTHERSRC)
COBJ := $(patsubst %.c,%.o,$(CSRC))
SOBJ := $(patsubst %.S,%.o,$(SSRC))
-LIB = libcore.a
-LIBS = $(LIB) $(com32)/lib/libcomcore.a $(LIBGCC)
-LIBOBJS = $(COBJ) $(SOBJ)
+# To make this compatible with the following $(filter-out), make sure
+# we prefix everything with ./
+CORE_PXE_CSRC = \
+ $(addprefix ./fs/pxe/, dhcp_option.c pxe.c tftp.c urlparse.c)
+
+LPXELINUX_CSRC = $(CORE_PXE_CSRC) \
+ $(shell find ./lwip -name '*.c' -print) \
+ $(addprefix ./fs/pxe/, \
+ core.c dnsresolv.c ftp.c ftp_readdir.c gpxeurl.c http.c \
+ http_readdir.c idle.c isr.c tcp.c)
+
+PXELINUX_CSRC = $(CORE_PXE_CSRC) \
+ $(shell find ./legacynet -name '*.c' -print)
+
+LPXELINUX_OBJS = $(LPXELINUX_CSRC:%.c=%.o)
+PXELINUX_OBJS = $(PXELINUX_CSRC:%.c=%.o)
+
+# Don't include console and network stack specific objects
+FILTER_OBJS = ./rawcon.o ./plaincon.o ./localboot.o ./pxeboot.o \
+ $(LPXELINUX_OBJS) $(PXELINUX_OBJS)
+COBJS = $(filter-out $(FILTER_OBJS),$(COBJ))
+SOBJS = $(filter-out $(FILTER_OBJS),$(SOBJ))
+
+LIB = libcom32.a
+LIBS = $(LIB) --whole-archive $(com32)/lib/libcom32core.a
+LIBDEP = $(filter-out -% %start%,$(LIBS))
+LIBOBJS = $(COBJS) $(SOBJS)
NASMDEBUG = -g -F dwarf
NASMOPT += $(NASMDEBUG)
PREPCORE = ../lzo/prepcore
-# CFLAGS += -DDEBUG=1
+CFLAGS += -D__SYSLINUX_CORE__
# The DATE is set on the make command line when building binaries for
# official release. Otherwise, substitute a hex string that is pretty much
@@ -78,7 +103,7 @@ kwdhash.gen: keywords genhash.pl
.PRECIOUS: %.elf
%.raw: %.elf
- $(OBJCOPY) -O binary $< $(@:.bin=.raw)
+ $(OBJCOPY) -O binary -S $< $(@:.bin=.raw)
# GNU make 3.82 gets confused by the first form
.PRECIOUS: %.raw
@@ -91,21 +116,64 @@ kwdhash.gen: keywords genhash.pl
-DHEXDATE="$(HEXDATE)" \
-l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
-%.elf: %.o $(LIBS) syslinux.ld
- $(LD) $(LDFLAGS) -T syslinux.ld -M -o $@ $< \
- --start-group $(LIBS) --end-group \
+AUXLIBS = libisolinux.a libisolinux-debug.a libldlinux.a \
+ libpxelinux.a liblpxelinux.a
+
+%.elf: %.o $(LIBDEP) syslinux.ld $(AUXLIBS)
+ $(LD) $(LDFLAGS) -Bsymbolic -pie -E --hash-style=gnu -T syslinux.ld -M -o $@ $< \
+ --start-group $(LIBS) lib$(patsubst %.elf,%.a,$@) --end-group \
> $(@:.elf=.map)
$(OBJDUMP) -h $@ > $(@:.elf=.sec)
$(PERL) lstadjust.pl $(@:.elf=.lsr) $(@:.elf=.sec) $(@:.elf=.lst)
+libisolinux.a: rawcon.o localboot.o
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
+libisolinux-debug.a: libisolinux.a
+ cp $^ $@
+
+# Legacy network stack
+libpxelinux.a: rawcon.o $(PXELINUX_OBJS) pxeboot.o
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
+# LwIP network stack
+liblpxelinux.a: rawcon.o $(LPXELINUX_OBJS) pxeboot.o
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
+libldlinux.a: plaincon.o localboot.o
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
$(LIB): $(LIBOBJS)
rm -f $@
$(AR) cq $@ $^
$(RANLIB) $@
+pxelinux.o: pxelinux.asm kwdhash.gen ../version.gen
+ $(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \
+ -DHEXDATE="$(HEXDATE)" \
+ -DIS_LPXELINUX=0 \
+ -l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
+
pxelinux.0: pxelinux.bin
cp -f $< $@
+lpxelinux.o: pxelinux.asm kwdhash.gen ../version.gen
+ $(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \
+ -DHEXDATE="$(HEXDATE)" \
+ -DIS_LPXELINUX=1 \
+ -l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
+
+lpxelinux.0: lpxelinux.bin
+ cp -f $< $@
+
ldlinux.bss: ldlinux.bin
dd if=$< of=$@ bs=512 count=1
@@ -126,10 +194,11 @@ install-all: install install-lib
netinstall: installer
tidy dist:
- rm -f codepage.cp *.o *.elf *.a stupid.* patch.offset .depend .*.d
+ find . -type f \( -name '*.o' -o -name '*.a' -o -name '.*.d' \
+ -o -name '*.lst' \) -print | xargs -rt rm -f
+ rm -f codepage.cp *.elf stupid.* patch.offset .depend
rm -f *.elf.tmp *.sym
- rm -f *.lsr *.lst *.map *.sec *.raw
- rm -f */*.o */*/*.o */*.lst */*/*.lst */.*.d */*/.*.d
+ rm -f *.lsr *.map *.sec *.raw
rm -f $(OBSOLETE) $(LIB)
clean: tidy
@@ -138,4 +207,4 @@ spotless: clean
rm -f $(BTARGET) *.bin *_bin.c
# Include dependencies file
--include .*.d */.*.d */*/.*.d
+-include $(shell find . -name '.*.d' -print)
diff --git a/core/abort.inc b/core/abort.inc
deleted file mode 100644
index 9b181363..00000000
--- a/core/abort.inc
+++ /dev/null
@@ -1,84 +0,0 @@
-; -----------------------------------------------------------------------
-;
-; Copyright 2005-2009 H. Peter Anvin - All Rights Reserved
-; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-; Boston MA 02111-1307, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; -----------------------------------------------------------------------
-
-;
-; abort.inc
-;
-; Code to terminate a kernel load
-;
-
- section .text16
-
-;
-; dot_pause: same as abort_check, except prints a dot, too
-; assumes CS == DS
-;
-dot_pause:
- push si
- mov si,dot_msg
- call writestr_qchk
- pop si
- ; fall through
-
-;
-; abort_check: let the user abort with <ESC> or <Ctrl-C>
-;
-abort_check:
- call reset_idle ; Not idle despite pollchar
- call pollchar
- jz .ret1
- pusha
- call getchar
- cmp al,27 ; <ESC>
- je .kill
- cmp al,3 ; <Ctrl-C>
- je .kill
-.ret2: popa
-.ret1: ret
-
-.kill: mov si,aborted_msg
- mov bx,enter_command
- jmp abort_load_chain
-
-;
-; abort_load: Called by various routines which wants to print a fatal
-; error message and return to the command prompt. Since this
-; may happen at just about any stage of the boot process, assume
-; our state is messed up, and just reset the segment registers
-; and the stack forcibly.
-;
-; SI = offset (in _text) of error message to print
-; BX = future entry point (abort_load_chain)
-;
-abort_load:
- mov bx,error_or_command
-abort_load_chain:
- RESET_STACK_AND_SEGS AX
- call writestr ; Expects SI -> error msg
-
- ; Return to the command prompt
- jmp bx
-
-;
-; error_or_command: Execute ONERROR if appropriate, otherwise enter_command
-;
-error_or_command:
- mov cx,[OnerrorLen]
- and cx,cx
- jnz on_error
- jmp enter_command
-
- section .data16
-aborted_msg db ' aborted.', CR, LF, 0
-
- section .text16
diff --git a/core/bios.inc b/core/bios.inc
index 33a3cd4c..2e150594 100644
--- a/core/bios.inc
+++ b/core/bios.inc
@@ -18,7 +18,6 @@
%ifndef _BIOS_INC
%define _BIOS_INC
- global BIOS_fbm, BIOS_timer
; Interrupt vectors
absolute 4*1Ch
diff --git a/core/bootsect.inc b/core/bootsect.inc
deleted file mode 100644
index 9e47e1a5..00000000
--- a/core/bootsect.inc
+++ /dev/null
@@ -1,253 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; bootsect.inc
-;;
-;; Load a boot sector (or other bootstrap program.)
-;;
-;; Unlike previous versions of this software, this doesn't require that
-;; the length is 512 bytes. This allows PXE bootstraps and WinNT
-;; "CD boot sectors" to be invoked.
-;;
-
-;
-; Load a boot sector
-;
-is_bootsector:
-%if IS_SYSLINUX
- ; Transfer zero bytes
- push word 0
- jmp short load_bootsec
-
-is_bss_sector:
- ; Transfer the superblock
-SuperSize equ $+1
- push word superblock_len_fat16
-%endif
-load_bootsec:
- mov edi,free_high_memory
- mov [trackbuf+4],edi ; Copy from this address
- mov eax,0xA0000 ; Maximum load
- xor dx,dx ; No padding
- mov bx,abort_check ; Don't print dots, but allow abort
- call load_high
-
- sub edi,free_high_memory
- mov [trackbuf+8],edi ; Save length
-
- cmp edi,0xA0000-7C00h
- ja bs_too_big
-
- mov eax,7C00h ; Entry point
- mov [trackbuf],eax ; Copy to this address
-
-%if IS_SYSLINUX
- xor ecx,ecx
- pop cx
-
- ; For a BSS boot sector we have to patch.
- mov esi,superblock
- mov edi,free_high_memory+(superblock-bootsec)
- call bcopy
-%endif
- push eax ; Save entry point
-
- xor edx,edx
- xor esi,esi
-%if IS_SYSLINUX || IS_EXTLINUX
- ; Restore original FDC table
- mov eax,[OrigFDCTabPtr]
- mov [fdctab],eax
-
- mov dl,[DriveNumber]
- mov si,PartInfo ; Partition info buffer
- mov di,800h-18 ; Put partition info here
- push di
- mov cx,8 ; 16 bytes
- xor ax,ax
- rep movsw
- pop si ; DS:SI points to partition info
- xor bx,bx
-%elif IS_ISOLINUX
- mov dl,[DriveNumber]
- xor bx,bx
-%elif IS_PXELINUX
- mov byte [KeepPXE],03h ; Chainloading + keep PXE
- pm_call reset_pxe
- lfs si,[InitStack]
- ; Put restore DS, EDX and ESI to the true initial values
- mov bx,[fs:si+6]
- mov edx,[fs:si+28]
- mov esi,[fs:si+12]
-%endif
-
-;
-; replace_bootstrap for the special case where we have exactly one
-; descriptor, based in low memory. We will generate a second descriptor
-; to clear remaining FBM.
-;
-
-replace_bootstrap_one:
- mov eax,[trackbuf] ; Base address
- add eax,[trackbuf+8] ; Length
- movzx ecx,word [BIOS_fbm]
- shl ecx,10 ; Free Base Memory
- sub ecx,eax
- mov [trackbuf+12],eax
- or dword [trackbuf+16],-1 ; Zero memory
- mov [trackbuf+20],ecx
- push word 2 ; Length of descriptor list
- ; Fall through
-
-;
-; Entrypoint for "shut down and replace bootstrap" -- also invoked by
-; the COMBOOT API. This routine expects the entry point (CS, IP) and the
-; count of the descriptor sequence on the stack; the shuffle
-; descriptors start at the first byte of the trackbuf.
-;
-; The registers EDX and ESI are passed on to the called program,
-; and BX is passed on as DS.
-;
-replace_bootstrap:
- ;
- ; Prepare for shutting down
- ;
- call vgaclearmode
-
-;
-; We jump here when loading a kernel image, so that we don't reset
-; the screen mode in "quiet" mode
-;
-replace_bootstrap_noclearmode:
- call cleanup_hardware
-
- ;
- ; Set up initial stack frame (not used by PXE if keeppxe is
- ; set - we use the PXE stack then.)
- ;
- xor ax,ax
- mov ds,ax
- mov es,ax
-
-%if IS_PXELINUX
- cmp byte [KeepPXE],0
- je .stdstack
- les di,[InitStack] ; Reset stack to PXE original
- jmp .stackok
-%endif
-.stdstack:
- ; StackBuf is guaranteed to have 44 bytes free immediately
- ; above it, and it will not interfere with our existing stack.
- mov di,StackBuf
- push di
- mov cx,22 ; 44 bytes
- rep stosw
- pop di
-.stackok:
-
- mov [es:di+28],edx ; New EDX
- mov [es:di+12],esi ; New ESI
- mov [es:di+6],bx ; New DS
-
-%if IS_PXELINUX == 0
- ; DON'T DO THIS FOR PXELINUX...
- ; For PXE, ES:BX -> PXENV+, and this would corrupt
- ; that use.
-
- ; Restore ES:DI -> $PnP (if we were ourselves called
- ; that way...)
- mov ax,[OrigESDI]
- mov bx,[OrigESDI+2]
-
- mov [es:di+8],ax ; New DI
- mov [es:di+4],bx ; New ES
-%endif
- pop ax ; descriptor list entries count
-
- push di
- push es
-
- push ds
- pop es
-
- mov ebx,trackbuf
- imul di,ax,12
- push di ; length of list
- add di,bx ; DI <- end of list
-
- ; Terminating entry...
- lea eax,[replace_stub] ; Entrypoint
- push ax
- stosd
- xor ax,ax ; EAX[31:16] == 0 already
- stosd ; 16-bit mode
- stosd ; End of list
-
- ; Copy the stub
- pop di
- mov si,__replacestub_lma
- mov cx,__replacestub_dwords
- rep movsd
-
- ; ECX <- final list length
- xor ecx,ecx
- pop cx ; original length in bytes
- add cx, 12 ; + termination entry size
-
- pop word [replace_stub.ss]
- pop word [replace_stub.esp]
- pop dword [replace_stub.csip]
-
- cli
- mov ss,[replace_stub.ss]
- mov esp,[replace_stub.esp]
-
- mov edi,trackbuf
- mov esi,edi
-
- jmp shuffle_and_boot_raw
-
- ; This stub gets run after the shuffle. It is copied
- ; below 0x7c00 in order to properly handle the case
- ; of bootstrap replacement.
- section .replacestub
-replace_stub:
- mov cr0,eax
- jmp 0:.next
-.next:
- mov ax,strict word 0
-.ss equ $-2
- mov ss,ax
- mov esp,strict dword 0
-.esp equ $-4
- pop gs
- pop fs
- pop es
- pop ds
- popad
- popfd
- jmp 0:0
-.csip equ $-4
-
- section .text16
-bs_too_big:
- call close
- mov si,err_bs_too_big
- jmp abort_load
-
- section .data16
-err_bs_too_big db "Too large for a bootstrap (need LINUX instead of KERNEL?)"
- db CR, LF, 0
-
- section .text16
diff --git a/core/call16.c b/core/call16.c
index 095f814f..3ef6690c 100644
--- a/core/call16.c
+++ b/core/call16.c
@@ -20,7 +20,7 @@
#include <stdio.h>
#include "core.h"
-const com32sys_t zero_regs; /* Common all-zero register set */
+__export const com32sys_t zero_regs; /* Common all-zero register set */
static inline uint32_t eflags(void)
{
@@ -30,7 +30,8 @@ static inline uint32_t eflags(void)
return v;
}
-void call16(void (*func)(void), const com32sys_t *ireg, com32sys_t *oreg)
+__export void call16(void (*func)(void), const com32sys_t *ireg,
+ com32sys_t *oreg)
{
com32sys_t xreg = *ireg;
diff --git a/core/callback.inc b/core/callback.inc
index d98d8008..f1332e8e 100644
--- a/core/callback.inc
+++ b/core/callback.inc
@@ -37,18 +37,19 @@
; - Return segment (== real mode cs == 0)
; - Return flags
;
- global core_farcall
+ global core_farcall:function hidden
core_farcall:
mov eax,[esp+1*4] ; CS:IP
jmp core_syscall
- global core_intcall
+ global core_intcall:function hidden
core_intcall:
movzx eax,byte [esp+1*4] ; INT number
mov eax,[eax*4] ; Get CS:IP from low memory
core_syscall:
pushfd ; Save IF among other things...
+ inc dword [CallbackCtr]
push ebx
push ebp
push esi
@@ -130,6 +131,10 @@ core_syscall:
; Remove from stack
pop dword [CallbackSP]
+ dec dword [CallbackCtr]
+ jnz .skip
+ call [core_pm_hook]
+.skip:
pop edi
pop esi
pop ebp
@@ -142,9 +147,10 @@ core_syscall:
; followed by the return CS:IP and the CS:IP of the target function.
; The value of IF is copied from the calling routine.
;
- global core_cfarcall
+ global core_cfarcall:function hidden
core_cfarcall:
pushfd ; Save IF among other things...
+ inc dword [CallbackCtr]
push ebx
push ebp
push esi
@@ -199,6 +205,10 @@ core_cfarcall:
mov eax,esi
; EDX already set up to be the RM return value
pop dword [CallbackSP]
+ dec dword [CallbackCtr]
+ jnz .skip
+ call [core_pm_hook]
+.skip:
pop ebx
pop ebp
pop esi
@@ -206,10 +216,11 @@ core_cfarcall:
popfd
ret
- bits 16
section .bss16
alignb 4
+ global core_pm_hook
CallbackSP resd 1 ; SP saved during callback
+CallbackCtr resd 1
bits 16
section .text16
diff --git a/core/cleanup.c b/core/cleanup.c
new file mode 100644
index 00000000..73b63dbf
--- /dev/null
+++ b/core/cleanup.c
@@ -0,0 +1,46 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ */
+#include <com32.h>
+#include <core.h>
+
+extern void timer_cleanup(void);
+extern void comboot_cleanup_api(void);
+
+/*
+ * cleanup.c
+ *
+ * Some final tidying before jumping to a kernel or bootsector
+ */
+
+/*
+ * cleanup_hardware:
+ *
+ * Shut down anything transient.
+ */
+__export void cleanup_hardware(void)
+{
+ /*
+ * TODO
+ *
+ * Linux wants the floppy motor shut off before starting the
+ * kernel, at least bootsect.S seems to imply so. If we don't
+ * load the floppy driver, this is *definitely* so!
+ */
+ __intcall(0x13, &zero_regs, NULL);
+
+ call16(comboot_cleanup_api, &zero_regs, NULL);
+ call16(timer_cleanup, &zero_regs, NULL);
+
+ /* If we enabled serial port interrupts, clean them up now */
+ sirq_cleanup();
+}
diff --git a/core/cleanup.inc b/core/cleanup.inc
deleted file mode 100644
index 300584c7..00000000
--- a/core/cleanup.inc
+++ /dev/null
@@ -1,60 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; cleanup.inc
-;;
-;; Some final tidying before jumping to a kernel or bootsector
-;;
-
- section .text16
-;
-; cleanup_hardware:
-;
-; Shut down anything transient. *No segment assumptions*.
-; Preserves all registers.
-;
-cleanup_hardware:
- pushad
-;
-; Linux wants the floppy motor shut off before starting the kernel,
-; at least bootsect.S seems to imply so. If we don't load the floppy
-; driver, this is *definitely* so!
-;
- xor ax,ax
- xor dx,dx
- int 13h
-
-%if 0 ; This bug report has not been substantiated!
-; Vmware crashes if we scroll in the decompressor! Try to detect vmware
-; and if it is Vmware, clear the screen...
- mov eax,'VMXh'
- xor ebx, ebx
- mov ecx, 10 ; Get version
- mov dx, 'VX'
- in eax, dx
- cmp ebx, 'VMXh'
- jne .no_vmware
-
- mov ax,0x0003 ; Set mode (clear screen/home cursor)
- int 10h
-.no_vmware:
-%endif
-
- call comboot_cleanup_api
-
- call timer_cleanup
-
- popad
-
- ; If we enabled serial port interrupts, clean them up now
- jmp sirq_cleanup
diff --git a/core/cmdline.inc b/core/cmdline.inc
deleted file mode 100644
index 3e63f9ab..00000000
--- a/core/cmdline.inc
+++ /dev/null
@@ -1,101 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; cmdline.inc
-;;
-;; Common routine to assemble [null-terminated] command line into
-;; real_mode_seg:cmd_line_here.
-;; Not used by plain kernel due to BOOT_IMAGE= etc.
-;;
-
- section .text16
-
-;
-; Assumes DS == CS
-;
-make_plain_cmdline:
- push es
- ; ui.inc has already copied any APPEND options
- mov ax,real_mode_seg
- mov es,ax
-
- mov di,[CmdLinePtr]
- call do_ip_append
-
- mov si,[CmdOptPtr]
-
- call strcpy
-
- dec di
- mov [CmdLinePtr],di
- mov byte [es:di],0 ; Null-terminate
-
- pop es
- ret
-
-;
-; Actual IPAppend strings...
-;
-%if IS_PXELINUX
- extern IPOption, BOOTIFStr, SYSUUIDStr
- global IPAppends, numIPAppends
-
- section .data16
- alignz 2
-IPAppends dw IPOption
- dw BOOTIFStr
- dw SYSUUIDStr
-numIPAppends equ ($-IPAppends)/2
-%else
-IPAppends equ 0
-numIPAppends equ 0
-%endif
-
-;
-; Handle "ipappend" strings, if applicable
-;
-; Assumes DS == CS; pushes output to ES:DI
-;
- section .text16
-
-do_ip_append:
-%ifndef DEPEND
- %if numIPAppends > 0
- push cx
- push bx
- push si
-
- mov bx,IPAppends
- mov cx,[IPAppend]
- and cx,(1 << numIPAppends)-1
-.loop:
- jcxz .done
- mov si,[bx]
- inc bx
- inc bx
- test cl,1
- jz .not_this
-
- call strcpy
- mov byte [es:di-1],' ' ; Replace final null with space
-.not_this:
- shr cx,1
- jmp .loop
-.done:
- pop si
- pop bx
- pop cx
- %endif
-%endif
- ret
diff --git a/core/com32.inc b/core/com32.inc
index 111590c3..9c565f1d 100644
--- a/core/com32.inc
+++ b/core/com32.inc
@@ -31,91 +31,8 @@
;
com32_entry equ free_high_memory
- section .text16
-is_com32_image:
- push si ; Save file handle
- push eax ; Save file length
-
- call make_plain_cmdline
- ; Copy the command line into the low cmdline buffer
- mov ax,real_mode_seg
- mov fs,ax
- mov si,cmd_line_here
- mov di,command_line
- mov cx,[CmdLinePtr]
- inc cx ; Include final null
- sub cx,si
- fs rep movsb
-
- mov si,KernelName
- mov di,Com32Name
- call strcpy
-
- call comboot_setup_api ; Set up the COMBOOT-style API
-
- mov edi,com32_entry ; Load address
- pop eax ; File length
- pop si ; File handle
- xor dx,dx ; No padding
- mov bx,abort_check ; Don't print dots, but allow abort
- call load_high
-
- mov esi,com32_entry
- mov edi,trackbuf
- mov ecx,5
- call bcopy
- cmp dword [trackbuf],0xcd4cfeb8
- jne not_com32r
- cmp byte [trackbuf+4],0x21
- jne not_com32r
-
-com32_start:
- ;
- ; Point the stack to the end of (permitted) high memory
- ;
- mov eax,[HighMemRsvd]
- xor ax,ax ; Align to a 64K boundary
- mov [PMESP],eax
- mov ebx,.pm ; Where to go in PM
- jmp enter_pm
-
-;
-; This is invoked right before the actually starting the COM32
-; progam, in 32-bit mode...
-;
- bits 32
- section .text
-.pm:
- ; Set up the calling stack frame
-
- push dword pm_api_vector
- push dword Com32Name ; Module filename
- push dword [HighMemSize] ; Memory managed by Syslinux
- push dword core_cfarcall ; Cfarcall entry point
- push dword core_farcall ; Farcall entry point
- push dword (1 << 16) ; 64K bounce buffer
- push dword core_real_mode ; Bounce buffer address
- push dword core_intcall ; Intcall entry point
- push dword command_line ; Command line pointer
- push dword 9 ; Argument count
- sti ; Interrupts OK now
- call com32_entry ; Run the program...
- ; ... on return, fall through to com32_exit ...
-com32_exit:
- mov bx,comboot_return
- jmp enter_rm
-
bits 16
- section .text16
-not_com32r:
- mov si,KernelName
- call writestr
- mov si,not_com32r_msg
- call writestr
- jmp enter_command
-
section .data16
-not_com32r_msg db ': not a COM32R image', CR, LF, 0
; Ersatz com32 invocation structure, to make libcom32
; code run the same if linked to the core. This is in
@@ -123,7 +40,7 @@ not_com32r_msg db ': not a COM32R image', CR, LF, 0
;
; Danger, Will Robinson: it's not clear the use of
; core_xfer_buf is safe here.
- global __entry_esp, __com32
+ global __com32:data hidden
alignz 4
__entry_esp:
dd 0 ; Dummy to avoid _exit issues
diff --git a/core/comboot.inc b/core/comboot.inc
index 61c8a3b7..f6270a8c 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -16,7 +16,6 @@
;;
;; Common code for running a COMBOOT image
;;
-
section .text16
; Parameter registers definition; this is the definition
@@ -61,83 +60,6 @@
%define P_DI word [bp]
%define P_HDI word [bp+2]
-; Looks like a COMBOOT image but too large
-comboot_too_large:
- pm_call pm_close_file
- mov si,err_comlarge
- call writestr
- jmp enter_command
-
-;
-; Load a COMBOOT image. A COMBOOT image is basically a DOS .COM file,
-; except that it may, of course, not contain any DOS system calls. We
-; do, however, allow the execution of INT 20h to return to SYSLINUX.
-;
-is_comboot_image:
- push si ; Save file handle
-
- call make_plain_cmdline
-
- call comboot_setup_api
-
- mov cx,comboot_seg
- mov es,cx
-
- xor di,di
- mov cx,64 ; 256 bytes (size of PSP)
- xor eax,eax ; Clear PSP
- rep stosd
-
- mov word [es:0], 020CDh ; INT 20h instruction
- ; First non-free paragraph
- ; This is valid because comboot_seg == real_mode_seg
- ; == the highest segment used by all derivatives
- int 12h ; Get DOS memory size
- shl ax,6 ; Kilobytes -> paragraphs
- mov word [es:02h],ax
-
-%ifndef DEPEND
-%if real_mode_seg != comboot_seg
-%error "This code assumes real_mode_seg == comboot_seg"
-%endif
-%endif
- ; Copy the command line from high memory
- mov si,cmd_line_here
- mov cx,125 ; Max cmdline len (minus space and CR)
- mov di,081h ; Offset in PSP for command line
- mov al,' ' ; DOS command lines begin with a space
- stosb
-
-.loop: es lodsb
- and al,al
- jz .done
- stosb
- loop .loop
-.done:
-
- mov al,0Dh ; CR after last character
- stosb
- mov ax,di
- sub al,82h ; Include space but not CR
- mov [es:80h],al ; Store command line length
-
- ; Now actually load the file...
- pop si ; File handle
- mov bx,100h ; Load at <seg>:0100h
- mov cx,0FF00h ; Maximum number of bytes
- pm_call getfsbytes
- cmp ecx,65536-256-2 ; Maximum size
- ja comboot_too_large
-
- ; And invoke the program...
- mov ax,es
- mov ds,ax
- mov ss,ax
- xor sp,sp
- push word 0 ; Return to address 0 -> exit
-
- jmp comboot_seg:100h ; Run it
-
;
; Set up the COMBOOT API interrupt vectors. This is now done at
; initialization time.
@@ -173,6 +95,7 @@ comboot_setup_api:
; Restore the original state of the COMBOOT API vectors, and free
; any low memory allocated by the comboot module.
;
+ global comboot_cleanup_api:function hidden
comboot_cleanup_api:
pusha
mov si,DOSSaveVectors
@@ -191,7 +114,7 @@ DOSSaveVectors resd 32
comboot_vectors:
dw comboot_return ; INT 20 = exit
- dw comboot_int21 ; INT 21 = DOS-compatible system calls
+ dw comboot_err(21h) ; INT 21 = DOS-compatible system calls
dw comboot_int22 ; INT 22 = native system calls
dw comboot_err(23h) ; INT 23 = DOS Ctrl-C handler
dw comboot_err(24h) ; INT 24 = DOS critical error handler
@@ -225,31 +148,6 @@ comboot_vectors:
section .text16
-; INT 21h: generic DOS system call
-comboot_int21: sti
- push ds
- push es
- push fs
- push gs
- pushad
- cld
- mov bp,cs
- mov ds,bp
- mov es,bp
- mov bp,sp ; Set up stack frame
-
- call adjust_screen ; The COMBOOT program might have changed the screen
-
- mov cx,int21_count
- mov si,int21_table
-.again: lodsb
- cmp al,P_AH
- lodsw
- loopne .again
- ; The last function in the list is the
- ; "no such function" function
- clc
- call ax ; Call the invoked function
comboot_resume:
mov bp,sp ; In case the function clobbers BP
setc P_FLAGSL ; Propagate CF->error
@@ -276,20 +174,20 @@ comboot_bogus: pop dx ; Interrupt number
jmp comboot_exit_msg
comboot_bogus_tail:
xchg ax,dx
- call writehex2 ; Interrupt number
+ pm_call pm_writehex2 ; Interrupt number
mov al,' '
- call writechr
+ pm_call pm_writechr
xchg ax,dx
- call writehex4 ; Function number (AX)
+ pm_call pm_writehex4 ; Function number (AX)
mov al,' '
- call writechr
+ pm_call pm_writechr
mov eax,edi
- call writehex8 ; CS:IP of the origin
- call crlf
- jmp enter_command
+ pm_call pm_writehex8 ; CS:IP of the origin
+ pm_call crlf
+ jmp kaboom
; Proper return vector
-; Note: this gets invoked both via INT 21h and directly via INT 20h.
+; Note: this gets invoked directly via INT 20h.
; We don't need to cld explicitly here, because comboot_exit does that
; when invoking RESET_STACK_AND_SEGS.
comboot_return:
@@ -309,12 +207,12 @@ comboot_exit_msg:
pop bx ; Return address
RESET_STACK_AND_SEGS si ; Contains sti, cld
pm_call comboot_cleanup_lowmem
- call adjust_screen ; The COMBOOT program might have changed the screen
+ pm_call pm_adjust_screen; The COMBOOT program might have change the screen
jcxz .nomsg
mov si,KernelName
- call writestr
+ pm_call pm_writestr
mov si,cx
- call writestr
+ pm_call pm_writestr
.nomsg:
jmp bx
@@ -322,22 +220,22 @@ comboot_exit_msg:
; INT 21h system calls
;
comboot_getkey: ; 01 = get key with echo
- call vgashowcursor
+ pm_call vgashowcursor
call comboot_getchar
- call vgahidecursor
- call writechr
+ pm_call vgahidecursor
+ pm_call pm_writechr
clc
ret
comboot_writechr: ; 02 = writechr
mov al,P_DL
- call writechr
+ pm_call pm_writechr
clc
ret
comboot_writeserial: ; 04 = write serial port
mov al,P_DL
- call write_serial
+ pm_call pm_write_serial
clc
ret
@@ -352,7 +250,7 @@ comboot_writestr: ; 09 = write DOS string
.loop: es lodsb
cmp al,'$' ; End string with $ - bizarre
je .done
- call writechr
+ pm_call pm_writechr
jmp short .loop
.done: clc
ret
@@ -360,7 +258,7 @@ comboot_writestr: ; 09 = write DOS string
comboot_checkkey: ; 0B = check keyboard status
cmp byte [APIKeyFlag],00h
jnz .waiting
- call pollchar
+ pm_call pm_pollchar
.waiting: setz al
dec al ; AL = 0FFh if present, 0 if not
mov P_AL,al
@@ -379,7 +277,7 @@ comboot_checkver: ; 30 = check DOS version
comboot_getchar:
cmp byte [APIKeyFlag],00h
jne .queued
- call getchar ; If not queued get input
+ pm_call pm_getchar ; If not queued get input
and al,al ; Function key? (CF <- 0)
jnz .done
mov [APIKeyWait],ah ; High part of key
@@ -396,7 +294,8 @@ comboot_getchar:
comboot_int28:
sti
cld
- call do_idle
+ extern __idle
+ pm_call __idle
iret
;
@@ -405,7 +304,7 @@ comboot_int28:
comboot_int29:
sti
cld
- call writechr ; Preserves registers!
+ pm_call pm_writechr
iret
;
@@ -425,7 +324,7 @@ comboot_int22:
mov es,bp
mov bp,sp ; Set up stack frame
- call adjust_screen ; The COMBOOT program might have changed the screen
+ pm_call pm_adjust_screen ; The COMBOOT program might hav changed the screen
cmp ax,int22_count
jb .ok
@@ -444,127 +343,6 @@ comapi_err:
ret
;
-; INT 22h AX=0001h Get SYSLINUX version
-;
-comapi_get_version:
- ; Number of API functions supported
- mov P_AX,int22_count
- ; SYSLINUX version
- mov P_CX,(VERSION_MAJOR << 8)+VERSION_MINOR
- ; SYSLINUX derivative ID byte
- mov P_DX,my_id
- ; For future use
- mov P_BX,cs ; cs == 0
-
- mov P_ES,ds
- ; ES:SI -> version banner
- mov P_SI,syslinux_banner + 2 ; Skip leading CR LF
- ; ES:DI -> copyright string
- mov P_DI,copyright_str + 1 ; Skip leading space
-
-comapi_nop:
- clc
- ret
-
-;
-; INT 22h AX=0002h Write string
-;
-; Write null-terminated string in ES:BX
-;
-comapi_writestr:
- mov ds,P_ES
- mov si,P_BX
- call writestr
- clc
- ret
-
-;
-; INT 22h AX=0003h Run command
-;
-; Terminates the COMBOOT program and executes the command line in
-; ES:BX as if it had been entered by the user.
-;
-comapi_run:
- mov ds,P_ES
- mov si,P_BX
- mov di,command_line
- call strcpy
- push load_kernel ; Run a new kernel
- jmp comboot_exit ; Terminate task, clean up
-
-;
-; INT 22h AX=0004h Run default command
-;
-; Terminates the COMBOOT program and executes the default command line
-; as if a timeout had happened or the user pressed <Enter>.
-;
-comapi_run_default:
- push auto_boot
- jmp comboot_exit
-
-;
-; INT 22h AX=0005h Force text mode
-;
-; Puts the video in standard text mode
-;
-comapi_textmode:
- call vgaclearmode
- clc
- ret
-
-;
-; INT 22h AX=0006h Open file
-;
-comapi_open:
- mov es,P_ES
- mov si,P_SI
- pm_call pm_open_file
- mov P_EAX,eax
- mov P_CX,cx
- mov P_SI,si
- ret
-
-;
-; INT 22h AX=0007h Read file
-;
-comapi_read:
- mov es,P_ES
- mov bx,P_BX
- mov si,P_SI
- mov cx,P_CX
- pm_call getfssec
- jnc .noteof
- xor si,si ; SI <- 0 on EOF, CF <- 0
-.noteof: mov P_SI,si
- mov P_ECX,ecx
- ret
-
-;
-; INT 22h AX=0008h Close file
-;
-comapi_close:
- mov si,P_SI
- pm_call pm_close_file
- clc
- ret
-
-;
-; INT 22h AX=0009h Call PXE stack
-;
-%if IS_PXELINUX
-comapi_pxecall:
- mov bx,P_BX
- mov es,P_ES
- mov di,P_DI
- call pxenv
- mov ax,[PXEStatus]
- mov P_AX,ax
- ret
-%else
-comapi_pxecall equ comapi_err ; Not available
-%endif
-
-;
; INT 22h AX=000Ah Get Derivative-Specific Info
;
comapi_derinfo:
@@ -611,243 +389,6 @@ comapi_derinfo:
ret
;
-; INT 22h AX=000Bh Get Serial Console Configuration
-;
-comapi_serialcfg:
- mov ax,[SerialPort]
- mov P_DX,ax
- mov ax,[BaudDivisor]
- mov P_CX,ax
- mov ax,[FlowControl]
- or al,ah
- mov ah,[FlowIgnore]
- shr ah,4
- test byte [DisplayCon],01h
- jnz .normalconsole
- or ah,80h
-.normalconsole:
- mov P_BX,ax
- clc
- ret
-
-;
-; INT 22h AX=000Ch Perform final cleanup
-;
-comapi_cleanup:
-%if IS_PXELINUX
- ; Unload PXE if requested
- test dl,3
- setnz [KeepPXE]
- sub bp,sp ; unload_pxe may move the stack around
- pm_call unload_pxe
- add bp,sp ; restore frame pointer...
-%elif IS_SYSLINUX || IS_EXTLINUX
- ; Restore original FDC table
- mov eax,[OrigFDCTabPtr]
- mov [fdctab],eax
-%endif
- call cleanup_hardware
- clc
- ret
-
-;
-; INT 22h AX=000Dh Obsolete
-;
-
-;
-; INT 22h AX=000Eh Get configuration file name
-;
-comapi_configfile:
- mov P_ES,cs
- mov P_BX,ConfigName
- clc
- ret
-
-;
-; INT 22h AX=000Fh Get IPAPPEND strings
-;
-comapi_ipappend:
- mov P_ES,cs
- mov P_CX,numIPAppends
- mov P_BX,IPAppends
- clc
- ret
-
-;
-; INT 22h AX=0010h Resolve hostname
-;
-%if IS_PXELINUX
- extern pxe_dns_resolv
-comapi_dnsresolv:
- mov ds,P_ES
- mov si,P_BX
- pm_call pxe_dns_resolv
- mov P_EAX,eax
- clc
- ret
-%else
-comapi_dnsresolv equ comapi_err
-%endif
-
- section .text16
-
-;
-; INT 22h AX=0011h Obsolete
-;
-
-;
-; INT 22h AX=0012h Obsolete
-;
-
-;
-; INT 22h AX=0013h Idle call
-;
-comapi_idle:
- call do_idle
- clc
- ret
-
-;
-; INT 22h AX=0014h Local boot
-;
-comapi_localboot:
- mov ax,P_DX
- jmp local_boot
-
-;
-; INT 22h AX=0015h Feature flags
-;
-comapi_features:
- mov P_ES,cs
- mov P_BX,feature_flags
- mov P_CX,feature_flags_len
- clc
- ret
-
-;
-; INT 22h AX=0016h Run kernel image
-;
-comapi_runkernel:
- mov al,P_DL
- cmp al,VK_TYPES-1
- ja .error
- mov [KernelType],al
-
- ; It's not just possible, but quite likely, that ES:BX
- ; points into real_mode_seg or xfer_buf_seg, so we
- ; need to exercise some special care here... use
- ; VKernelBuf for temporary storage.
- push ds
- mov ds,P_ES
- mov si,P_BX
- mov di,VKernelBuf
- call strcpy
- pop ds
-
- push ds
- mov ds,P_DS
- mov si,P_SI
- mov di,KernelName
- pm_call pm_mangle_name
- pop ds
- pm_call pm_searchdir
- jz comapi_err
-
- ; The kernel image was found, so we can load it...
- mov [Kernel_SI],si
- mov [Kernel_EAX],eax
-
-%if IS_PXELINUX
- mov al,P_CL
- mov [IPAppend],al
-%endif
-
- call comboot_exit
-
-.finish:
- ; Copy the command line into its proper place
- push es
- mov dx,real_mode_seg
- mov es,dx
- mov si,VKernelBuf
- mov di,cmd_line_here
- call strcpy
- mov word [es:di-1],' ' ; Simulate APPEND: space plus null
- pop es
- mov [CmdLinePtr],di
- mov word [CmdOptPtr],zero_string
- jmp kernel_good_saved
-
-.error equ comapi_usingvga.error
-
-;
-; INT 22h AX=0017h Report video mode change
-;
-comapi_usingvga:
- mov ax,P_BX
- cmp ax,0Fh ; Unknown flags = failure
- ja .error
- mov [UsingVGA],al
- mov cx,P_CX
- mov dx,P_DX
- mov [GXPixCols],cx
- mov [GXPixRows],dx
- test al,08h
- jnz .notext
- call adjust_screen
-.notext:
- clc
- ret
-.error:
- stc
- ret
-
-;
-; INT 22h AX=0018h Query custom font
-;
-comapi_userfont:
- mov al,[UserFont]
- and al,al
- jz .done
- mov al,[VGAFontSize]
- mov P_ES,aux_seg
- mov P_BX,aux.fontbuf
-
-.done: ; CF=0 here
- mov P_AL,al
- ret
-
-;
-; INT 22h AX=0019h Read disk
-;
-%if IS_SYSLINUX || IS_ISOLINUX || IS_EXTLINUX
-comapi_readdisk:
- cmp P_EDI,0 ; Reserved for future expansion
- jnz .err
- mov eax,P_EDX
- mov edx,P_ESI
- mov es,P_ES
- mov bx,P_BX
- mov bp,P_CX ; WE CANNOT use P_* after touching bp!
- call getlinsec
- clc
- ret
-.err:
- stc
- ret
-%else
-comapi_readdisk equ comapi_err
-%endif
-
-;
-; INT 22h AX=001Ah Obsolete
-;
-
-;
-; INT 22h AX=001Bh Obsolete
-;
-
-;
; INT 22h AX=001Ch Get pointer to auxillary data vector
;
comapi_getadv:
@@ -862,105 +403,76 @@ comapi_getadv:
comapi_writeadv equ adv_write
;
-; INT 22h AX=001Eh Keyboard remapping table
-comapi_kbdtable:
- cmp P_DX,0
- jne .err
- mov P_AX,1 ; Version
- mov P_CX,256 ; Length
- mov P_ES,cs
- mov P_BX,KbdMap
- ret
-.err:
- stc
- ret
-
-;
-; INT 22h AX=001Fh Get current working directory
-;
-comapi_getcwd:
- mov P_ES,cs
- mov P_BX,CurrentDirName
- clc
- ret
-
-;
-; INT 22h AX=0023h Query shuffler size
-;
-comapi_shufsize:
- ; +15 is padding to guarantee alignment
- mov P_CX,__bcopyxx_len + 15
- ret
-
-;
; INT 22h AX=0024h Cleanup, shuffle and boot raw
;
comapi_shufraw:
- call comapi_cleanup
+%if IS_PXELINUX
+ ; Unload PXE if requested
+ test dl,3
+ setnz [KeepPXE]
+ sub bp,sp ; unload_pxe may move the stack around
+ pm_call unload_pxe
+ add bp,sp ; restore frame pointer...
+%elif IS_SYSLINUX || IS_EXTLINUX
+ ; Restore original FDC table
+ mov eax,[OrigFDCTabPtr]
+ mov [fdctab],eax
+%endif
+ pm_call cleanup_hardware
mov edi,P_EDI
mov esi,P_ESI
mov ecx,P_ECX
jmp shuffle_and_boot_raw
- section .data16
+;
+; INT 22h AX=0025h Initialize the ADV structure
+;
+comapi_initadv:
+ call adv_init
+ ret
-%macro int21 2
- db %1
- dw %2
-%endmacro
-
-int21_table:
- int21 00h, comboot_return
- int21 01h, comboot_getkey
- int21 02h, comboot_writechr
- int21 04h, comboot_writeserial
- int21 08h, comboot_getkeynoecho
- int21 09h, comboot_writestr
- int21 0Bh, comboot_checkkey
- int21 30h, comboot_checkver
- int21 4Ch, comboot_return
- int21 -1, comboot_bad_int21
-int21_count equ ($-int21_table)/3
+ section .data16
alignz 2
int22_table:
dw comapi_err ; 0000 unimplemented syscall
- dw comapi_get_version ; 0001 get SYSLINUX version
- dw comapi_writestr ; 0002 write string
- dw comapi_run ; 0003 run specified command
- dw comapi_run_default ; 0004 run default command
- dw comapi_textmode ; 0005 force text mode
- dw comapi_open ; 0006 open file
- dw comapi_read ; 0007 read file
- dw comapi_close ; 0008 close file
- dw comapi_pxecall ; 0009 call PXE stack
+ dw comapi_err ; 0001 get SYSLINUX version
+ dw comapi_err ; 0002 write string
+ dw comapi_err ; 0003 run specified command
+ dw comapi_err ; 0004 run default command
+ dw comapi_err ; 0005 force text mode
+ dw comapi_err ; 0006 open file
+ dw comapi_err ; 0007 read file
+ dw comapi_err ; 0008 close file
+ dw comapi_err ; 0009 call PXE stack
dw comapi_derinfo ; 000A derivative-specific info
- dw comapi_serialcfg ; 000B get serial port config
- dw comapi_cleanup ; 000C perform final cleanup
+ dw comapi_err ; 000B get serial port config
+ dw comapi_err ; 000C perform final cleanup
dw comapi_err ; 000D clean up then bootstrap
- dw comapi_configfile ; 000E get name of config file
- dw comapi_ipappend ; 000F get ipappend strings
- dw comapi_dnsresolv ; 0010 resolve hostname
+ dw comapi_err ; 000E get name of config file
+ dw comapi_err ; 000F get ipappend strings
+ dw comapi_err ; 0010 resolve hostname
dw comapi_err ; 0011 maximum shuffle descriptors
dw comapi_err ; 0012 cleanup, shuffle and boot
- dw comapi_idle ; 0013 idle call
- dw comapi_localboot ; 0014 local boot
- dw comapi_features ; 0015 feature flags
- dw comapi_runkernel ; 0016 run kernel image
- dw comapi_usingvga ; 0017 report video mode change
- dw comapi_userfont ; 0018 query custom font
- dw comapi_readdisk ; 0019 read disk
+ dw comapi_err ; 0013 idle call
+ dw comapi_err ; 0014 local boot
+ dw comapi_err ; 0015 feature flags
+ dw comapi_err ; 0016 run kernel image
+ dw comapi_err ; 0017 report video mode change
+ dw comapi_err ; 0018 query custom font
+ dw comapi_err ; 0019 read disk
dw comapi_err ; 001A cleanup, shuffle and boot to pm
dw comapi_err ; 001B cleanup, shuffle and boot to rm
dw comapi_getadv ; 001C get pointer to ADV
dw comapi_writeadv ; 001D write ADV to disk
- dw comapi_kbdtable ; 001E keyboard remapping table
- dw comapi_getcwd ; 001F get current working directory
+ dw comapi_err ; 001E keyboard remapping table
+ dw comapi_err ; 001F get current working directory
dw comapi_err ; 0020 open directory
dw comapi_err ; 0021 read directory
dw comapi_err ; 0022 close directory
- dw comapi_shufsize ; 0023 query shuffler size
+ dw comapi_err ; 0023 query shuffler size
dw comapi_shufraw ; 0024 cleanup, shuffle and boot raw
+ dw comapi_initadv ; 0025 initialize adv structure
int22_count equ ($-int22_table)/2
APIKeyWait db 0
@@ -968,16 +480,6 @@ APIKeyFlag db 0
zero_string db 0 ; Empty, null-terminated string
-;
-; This is the feature flag array for INT 22h AX=0015h
-;
-; Note: PXELINUX clears the idle is noop flag if appropriate
-; in pxe_detect_nic_type
-;
-feature_flags:
- db 1 ; Have local boot, idle is not noop
-feature_flags_len equ ($-feature_flags)
-
err_notdos db ': attempted DOS system call INT ',0
err_comlarge db 'COMBOOT image too large.', CR, LF, 0
@@ -985,9 +487,7 @@ err_comlarge db 'COMBOOT image too large.', CR, LF, 0
alignb 4
DOSErrTramp resd 33 ; Error trampolines
- global ConfigName
-ConfigName resb FILENAME_MAX
%ifndef HAVE_CURRENTDIRNAME
- global CurrentDirName
+ global CurrentDirName:data hidden
CurrentDirName resb FILENAME_MAX
%endif
diff --git a/core/common.inc b/core/common.inc
index 7078011e..fd75dfe1 100644
--- a/core/common.inc
+++ b/core/common.inc
@@ -4,19 +4,9 @@
; some derivatives.)
;
-%include "getc.inc" ; getc et al
-%include "conio.inc" ; Console I/O
-%include "configinit.inc" ; Initialize configuration
-%include "parseconfig.inc" ; High-level config file handling
-%include "parsecmd.inc" ; Low-level config file handling
%include "pm.inc" ; Protected mode
%include "bcopy32.inc" ; 32-bit bcopy
-%include "loadhigh.inc" ; Load a file into high memory
-%include "font.inc" ; VGA font stuff
-%include "graphics.inc" ; VGA graphics
-%include "highmem.inc" ; High memory sizing
%include "strcpy.inc" ; strcpy()
-%include "idle.inc" ; Idle handling
%include "adv.inc" ; Auxillary Data Vector
%include "timer.inc" ; Timer handling
diff --git a/core/configinit.inc b/core/configinit.inc
deleted file mode 100644
index 915e77f7..00000000
--- a/core/configinit.inc
+++ /dev/null
@@ -1,51 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; configinit.inc
-;;
-;; Initialize the configuration section
-;;
-
- section .text16
-
-reset_config:
- call highmemsize
-
- ; Initialize the .config section
- xor eax,eax
- mov si,__config_lma
- mov di,__config_start
- mov cx,__config_dwords
- rep movsd
-
-%ifndef DEPEND
-%if NULLFILE != 0
- mov al,NULLFILE
- mov di,FKeyName
- mov cx,MAX_FKEYS*(1 << FILENAME_MAX_LG2)
- rep stosb
-%endif
-%endif
-
- mov di,KbdMap ; Default keymap 1:1
- xor al,al
- inc ch ; CX <- 256
-mkkeymap: stosb
- inc al
- loop mkkeymap
-
- mov eax,[HighMemSize]
- mov [VKernelEnd],eax
-
- ret
diff --git a/core/conio.c b/core/conio.c
new file mode 100644
index 00000000..d3428338
--- /dev/null
+++ b/core/conio.c
@@ -0,0 +1,266 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ *
+ *
+ * conio.c
+ *
+ * Console I/O code, except:
+ * writechr, writestr_early - module-dependent
+ * writestr, crlf - writestr.inc
+ * writehex* - writehex.inc
+ */
+#include <sys/io.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <fs.h>
+#include <com32.h>
+#include <sys/cpu.h>
+
+#include "bios.h"
+#include "graphics.h"
+
+union screen _cursor;
+union screen _screensize;
+
+/*
+ * Serial console stuff.
+ */
+__export uint16_t SerialPort = 0; /* Serial port base (or 0 for no serial port) */
+__export uint8_t FlowInput = 0; /* Input bits for serial flow */
+__export uint16_t BaudDivisor = 115200/9600; /* Baud rate divisor */
+__export uint8_t FlowIgnore = 0; /* Ignore input unless these bits set */
+__export uint16_t DisplayCon = 0x01; /* Display console enabled */
+__export uint8_t FlowOutput = 0; /* Output to assert for serial flow */
+
+__export uint8_t DisplayMask = 0x07; /* Display modes mask */
+
+uint8_t ScrollAttribute = 0x07; /* Grey on white (normal text color) */
+
+/*
+ * loadkeys: Load a LILO-style keymap
+ *
+ * Returns 0 on success, or -1 on error.
+ */
+__export int loadkeys(char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "r");
+ if (!f)
+ return -1;
+
+ fread(KbdMap, 1, sizeof(KbdMap), f);
+
+ fclose(f);
+ return 0;
+}
+
+/*
+ * write_serial: If serial output is enabled, write character on
+ * serial port.
+ */
+__export void write_serial(char data)
+{
+ if (!SerialPort)
+ return;
+
+ if (!(DisplayMask & 0x04))
+ return;
+
+ while (1) {
+ char ch;
+
+ ch = inb(SerialPort + 5); /* LSR */
+
+ /* Wait for space in transmit register */
+ if (!(ch & 0x20))
+ continue;
+
+ /* Wait for input flow control */
+ ch = inb(SerialPort + 6);
+ ch &= FlowInput;
+ if (ch != FlowInput)
+ continue;
+
+ break;
+ }
+
+ outb(data, SerialPort); /* Send data */
+ io_delay();
+}
+
+void pm_write_serial(com32sys_t *regs)
+{
+ write_serial(regs->eax.b[0]);
+}
+
+void pm_serialcfg(com32sys_t *regs)
+{
+ uint8_t al, ah;
+
+ regs->eax.w[0] = SerialPort;
+ regs->ecx.w[0] = BaudDivisor;
+
+ al = FlowOutput;
+ ah = FlowInput;
+
+ al |= ah;
+ ah = FlowIgnore;
+ ah >>= 4;
+
+ if (!DisplayCon)
+ ah |= 0x80;
+
+ regs->ebx.w[0] = al | (ah << 8);
+}
+
+/*
+ * write_serial_str: write_serial for strings
+ */
+__export void write_serial_str(char *data)
+{
+ char ch;
+
+ while ((ch = *data++))
+ write_serial(ch);
+}
+
+/*
+ * pollchar: check if we have an input character pending
+ *
+ * Returns 1 if character pending.
+ */
+__export int pollchar(void)
+{
+ com32sys_t ireg, oreg;
+ uint8_t data = 0;
+
+ memset(&ireg, 0, sizeof(ireg));
+
+ ireg.eax.b[1] = 0x11; /* Poll keyboard */
+ __intcall(0x16, &ireg, &oreg);
+
+ if (!(oreg.eflags.l & EFLAGS_ZF))
+ return 1;
+
+ if (SerialPort) {
+ cli();
+
+ /* Already-queued input? */
+ if (SerialTail == SerialHead) {
+ /* LSR */
+ data = inb(SerialPort + 5) & 1;
+ if (data) {
+ /* MSR */
+ data = inb(SerialPort + 6);
+
+ /* Required status bits */
+ data &= FlowIgnore;
+
+ if (data == FlowIgnore)
+ data = 1;
+ else
+ data = 0;
+ }
+ } else
+ data = 1;
+ sti();
+ }
+
+ return data;
+}
+
+void pm_pollchar(com32sys_t *regs)
+{
+ if (pollchar())
+ regs->eflags.l &= ~EFLAGS_ZF;
+ else
+ regs->eflags.l |= EFLAGS_ZF;
+}
+
+/*
+ * getchar: Read a character from keyboard or serial port
+ */
+__export char getchar(char *hi)
+{
+ com32sys_t ireg, oreg;
+ unsigned char data;
+
+ memset(&ireg, 0, sizeof(ireg));
+ memset(&oreg, 0, sizeof(oreg));
+ while (1) {
+ __idle();
+
+ ireg.eax.b[1] = 0x11; /* Poll keyboard */
+ __intcall(0x16, &ireg, &oreg);
+
+ if (oreg.eflags.l & EFLAGS_ZF) {
+ if (!SerialPort)
+ continue;
+
+ cli();
+ if (SerialTail != SerialHead) {
+ /* serial queued */
+ sti(); /* We already know we'll consume data */
+ data = *SerialTail++;
+
+ if (SerialTail > SerialHead + serial_buf_size)
+ SerialTail = SerialHead;
+ } else {
+ /* LSR */
+ data = inb(SerialPort + 5) & 1;
+ if (!data) {
+ sti();
+ continue;
+ }
+ data = inb(SerialPort + 6);
+ data &= FlowIgnore;
+ if (data != FlowIgnore) {
+ sti();
+ continue;
+ }
+
+ data = inb(SerialPort);
+ sti();
+ break;
+ }
+ } else {
+ /* Keyboard input? */
+ ireg.eax.b[1] = 0x10; /* Get keyboard input */
+ __intcall(0x16, &ireg, &oreg);
+
+ data = oreg.eax.b[0];
+ *hi = oreg.eax.b[1];
+
+ if (data == 0xE0)
+ data = 0;
+
+ if (data) {
+ /* Convert character sets */
+ data = KbdMap[data];
+ }
+ }
+
+ break;
+ }
+
+ reset_idle(); /* Character received */
+ return data;
+}
+
+void pm_getchar(com32sys_t *regs)
+{
+ regs->eax.b[0] = getchar((char *)&regs->eax.b[1]);
+}
diff --git a/core/conio.inc b/core/conio.inc
deleted file mode 100644
index b4505027..00000000
--- a/core/conio.inc
+++ /dev/null
@@ -1,431 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; conio.inc
-;;
-;; Console I/O code, except:
-;; writechr, writestr_early - module-dependent
-;; writestr, crlf - writestr.inc
-;; writehex* - writehex.inc
-;;
-
-;
-; loadkeys: Load a LILO-style keymap; file is open on the top of the
-; getc stack.
-;
- section .text16
-
-loadkeys:
- mov cx,256
- mov di,trackbuf
- call readc
- jc .done ; EOF already?
-
- ; Make sure we are at EOF now...
- call getc
- jnc .done ; We should be at EOF now!
-
- ; It was okay, we can now move it into the KbdMap
- mov si,trackbuf
- mov di,KbdMap
- mov cx,256 >> 2
- rep movsd
-
-.done:
- call close
- ret
-
-;
-; get_msg_file: Load a text file and write its contents to the screen,
-; interpreting color codes. Call with the file already
-; on the top of the open/getc stack.
-;
-; Assumes CS == DS == ES.
-;
-get_msg_file:
- mov byte [TextAttribute],07h ; Default grey on white
- mov byte [DisplayMask],07h ; Display text in all modes
- call msg_initvars
-
-print_msg_file:
-.getc:
- call getc
- jc .done
- cmp al,1Ah ; DOS EOF?
- je .done
- movzx cx,byte [UsingVGA]
- and cl,01h
- inc cx ; CL <- 01h = text mode,
- ; 02h = graphics mode
- call [NextCharJump] ; Do what shall be done
- jmp .getc
-.done:
- jmp close ; Tailcall!
-
-msg_putchar: ; Normal character
- cmp al,0Fh ; ^O = color code follows
- je msg_ctrl_o
- cmp al,0Dh ; Ignore <CR>
- je msg_ignore
- cmp al,0Ah ; <LF> = newline
- je msg_newline
- cmp al,0Ch ; <FF> = clear screen
- je msg_formfeed
- cmp al,07h ; <BEL> = beep
- je msg_beep
- cmp al,19h ; <EM> = return to text mode
- je msg_novga
- cmp al,18h ; <CAN> = VGA filename follows
- je msg_vga
- jnb .not_modectl
- cmp al,10h ; 10h to 17h are mode controls
- jae msg_modectl
-.not_modectl:
-
-msg_normal: call write_serial_displaymask ; Write to serial port
- test [DisplayMask],cl
- jz msg_ignore ; Not screen
- test byte [DisplayCon],01h
- jz msg_ignore
- mov bl,[TextAttribute]
- mov bh,[BIOS_page]
- mov ah,09h ; Write character/attribute
- mov cx,1 ; One character only
- int 10h ; Write to screen
- mov al,[CursorCol]
- inc ax
- cmp al,[VidCols]
- ja msg_line_wrap ; Screen wraparound
- mov [CursorCol],al
-
-msg_gotoxy: mov bh,[BIOS_page]
- mov dx,[CursorDX]
- mov ah,02h ; Set cursor position
- int 10h
-msg_ignore: ret
-
-msg_beep: mov ax,0E07h ; Beep
- xor bx,bx
- int 10h
- ret
-
-msg_ctrl_o: ; ^O = color code follows
- mov word [NextCharJump],msg_setbg
- ret
-msg_newline: ; Newline char or end of line
- mov si,crlf_msg
- call write_serial_str_displaymask
-msg_line_wrap: ; Screen wraparound
- test [DisplayMask],cl
- jz msg_ignore
- mov byte [CursorCol],0
- mov al,[CursorRow]
- inc ax
- cmp al,[VidRows]
- ja msg_scroll
- mov [CursorRow],al
- jmp short msg_gotoxy
-msg_scroll: xor cx,cx ; Upper left hand corner
- mov dx,[ScreenSize]
- mov [CursorRow],dh ; New cursor at the bottom
- mov bh,[ScrollAttribute]
- mov ax,0601h ; Scroll up one line
- int 10h
- jmp short msg_gotoxy
-msg_formfeed: ; Form feed character
- mov si,crff_msg
- call write_serial_str_displaymask
- test [DisplayMask],cl
- jz msg_ignore
- xor cx,cx
- mov [CursorDX],cx ; Upper lefthand corner
- mov dx,[ScreenSize]
- mov bh,[TextAttribute]
- mov ax,0600h ; Clear screen region
- int 10h
- jmp msg_gotoxy
-msg_setbg: ; Color background character
- call unhexchar
- jc msg_color_bad
- shl al,4
- test [DisplayMask],cl
- jz .dontset
- mov [TextAttribute],al
-.dontset:
- mov word [NextCharJump],msg_setfg
- ret
-msg_setfg: ; Color foreground character
- call unhexchar
- jc msg_color_bad
- test [DisplayMask],cl
- jz .dontset
- or [TextAttribute],al ; setbg set foreground to 0
-.dontset:
- jmp short msg_putcharnext
-msg_vga:
- mov word [NextCharJump],msg_filename
- mov di, VGAFileBuf
- jmp short msg_setvgafileptr
-
-msg_color_bad:
- mov byte [TextAttribute],07h ; Default attribute
-msg_putcharnext:
- mov word [NextCharJump],msg_putchar
- ret
-
-msg_filename: ; Getting VGA filename
- cmp al,0Ah ; <LF> = end of filename
- je msg_viewimage
- cmp al,' '
- jbe msg_ret ; Ignore space/control char
- mov di,[VGAFilePtr]
- cmp di,VGAFileBufEnd
- jnb msg_ret
- mov [di],al ; Can't use stosb (DS:)
- inc di
-msg_setvgafileptr:
- mov [VGAFilePtr],di
-msg_ret: ret
-
-msg_novga:
- call vgaclearmode
- jmp short msg_initvars
-
-msg_viewimage:
- mov si,[VGAFilePtr]
- mov byte [si],0 ; Zero-terminate filename
- mov si,VGAFileBuf
- mov di,VGAFileMBuf
- pm_call pm_mangle_name
- call core_open
- jz msg_putcharnext ; Not there
- call vgadisplayfile
- ; Fall through
-
- ; Subroutine to initialize variables, also needed
- ; after loading a graphics file
-msg_initvars:
- pusha
- mov bh,[BIOS_page]
- mov ah,03h ; Read cursor position
- int 10h
- mov [CursorDX],dx
- popa
- jmp short msg_putcharnext ; Initialize state machine
-
-msg_modectl:
- and al,07h
- mov [DisplayMask],al
- jmp short msg_putcharnext
-
-;
-; write_serial: If serial output is enabled, write character on serial port
-; write_serial_displaymask: d:o, but ignore if DisplayMask & 04h == 0
-;
-write_serial_displaymask:
- test byte [DisplayMask], 04h
- jz write_serial.end
-write_serial:
- pushfd
- pushad
- mov bx,[SerialPort]
- and bx,bx
- je .noserial
- push ax
- mov ah,[FlowInput]
-.waitspace:
- ; Wait for space in transmit register
- lea dx,[bx+5] ; DX -> LSR
- in al,dx
- test al,20h
- jz .waitspace
-
- ; Wait for input flow control
- inc dx ; DX -> MSR
- in al,dx
- and al,ah
- cmp al,ah
- jne .waitspace
-.no_flow:
-
- xchg dx,bx ; DX -> THR
- pop ax
- slow_out dx,al ; Send data
-.noserial: popad
- popfd
-.end: ret
-
-;
-; write_serial_str: write_serial for strings
-; write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0
-;
-write_serial_str_displaymask:
- test byte [DisplayMask], 04h
- jz write_serial_str.end
-
-write_serial_str:
-.loop lodsb
- and al,al
- jz .end
- call write_serial
- jmp short .loop
-.end: ret
-
-;
-; pollchar: check if we have an input character pending (ZF = 0)
-;
-pollchar:
- pushad
- mov ah,11h ; Poll keyboard
- int 16h
- jnz .done ; Keyboard response
- mov dx,[SerialPort]
- and dx,dx
- jz .done ; No serial port -> no input
- mov ax,[SerialTail] ; Already-queued input?
- cli
- cmp ax,[SerialHead]
- jne .done_sti ; If so, return ZF = 0
- add dx,5 ; DX -> LSR
- in al,dx
- test al,1 ; ZF = 0 if data pending
- jz .done_sti
- inc dx ; DX -> MSR
- mov ah,[FlowIgnore] ; Required status bits
- in al,dx
- and al,ah
- cmp al,ah
- setne al
- dec al ; Set ZF = 0 if equal
-.done_sti: sti
-.done: popad
- ret
-
-;
-; getchar: Read a character from keyboard or serial port
-;
-getchar.sti_again:
- sti
-getchar:
-.again:
- call do_idle
- mov ah,11h ; Poll keyboard
- int 16h
- jnz .kbd ; Keyboard input?
- mov bx,[SerialPort]
- and bx,bx
- jz .again
- mov ax,[SerialTail]
- cli
- cmp ax,[SerialHead]
- jne .serial_queued
- lea dx,[bx+5] ; DX -> LSR
- in al,dx
- test al,1
- jz .sti_again
- inc dx ; DX -> MSR
- mov ah,[FlowIgnore]
- in al,dx
- and al,ah
- cmp al,ah
- jne .sti_again
-.serial: xor ah,ah ; Avoid confusion
- mov dx,bx ; Data port
- in al,dx ; Read data
- sti
- jmp .done
-.serial_queued:
- sti ; We already know we'll consume data
- xchg bx,ax
- push ds
- mov ax,aux_seg + (aux.serial >> 4)
- mov ds,ax
- mov al,[bx]
- pop ds
- inc bx
- and bx,serial_buf_size-1
- mov [SerialTail],bx
- jmp .done
-
-.kbd: mov ah,10h ; Get keyboard input
- int 16h
- cmp al,0E0h
- jnz .not_ext
- xor al,al
-.not_ext:
- and al,al
- jz .func_key
- mov bx,KbdMap ; Convert character sets
- xlatb
-.func_key:
-.done:
- jmp reset_idle ; Character received
-
-%ifdef DEBUG_TRACERS
-;
-; debug hack to print a character with minimal code impact
-;
-debug_tracer: pushad
- pushfd
- mov bp,sp
- mov bx,[bp+9*4] ; Get return address
- mov al,[cs:bx] ; Get data byte
- inc word [bp+9*4] ; Return to after data byte
- call writechr
- popfd
- popad
- ret
-%endif ; DEBUG_TRACERS
-
- section .data16
-%if IS_ISOLINUX == 0 ; Defined elsewhere for ISOLINUX
-crlf_msg db CR, LF
-null_msg db 0
-%endif
-crff_msg db CR, FF, 0
-
- section .config
- ; This is a word to pc_setint16 can set it
-DisplayCon dw 01h ; Console display enabled
-
-ScrollAttribute db 07h ; Grey on white (normal text color)
-
- section .bss16
- alignb 2
-NextCharJump resw 1 ; Routine to interpret next print char
-CursorDX equ $
-CursorCol resb 1 ; Cursor column for message file
-CursorRow resb 1 ; Cursor row for message file
-ScreenSize equ $
-VidCols resb 1 ; Columns on screen-1
-VidRows resb 1 ; Rows on screen-1
-
-; Serial console stuff; don't put this in .config becasue we don't want
-; loading a new config file to undo this setting.
- section .data16
- alignz 4
-SerialPort dw 0 ; Serial port base (or 0 for no serial port)
-BaudDivisor dw 115200/9600 ; Baud rate divisor
-FlowControl equ $
-FlowOutput db 0 ; Outputs to assert for serial flow
-FlowInput db 0 ; Input bits for serial flow
-FlowIgnore db 0 ; Ignore input unless these bits set
-FlowDummy db 0 ; Unused
-
- section .bss16
-TextAttribute resb 1 ; Text attribute for message file
-DisplayMask resb 1 ; Display modes mask
-
- section .text16
-%include "serirq.inc"
diff --git a/core/console.c b/core/console.c
index 282c57f5..3b545bbd 100644
--- a/core/console.c
+++ b/core/console.c
@@ -1,18 +1,15 @@
#include <stddef.h>
#include <com32.h>
+#include <core.h>
#include <stdio.h>
#include <string.h>
void myputchar(int c)
{
- static com32sys_t ireg;
-
if (c == '\n')
myputchar('\r');
- ireg.eax.b[1] = 0x02;
- ireg.edx.b[0] = c;
- __intcall(0x21, &ireg, NULL);
+ writechr(c);
}
void myputs(const char *str)
diff --git a/core/debug.c b/core/debug.c
new file mode 100644
index 00000000..9bf8b3a6
--- /dev/null
+++ b/core/debug.c
@@ -0,0 +1,9 @@
+#include "core.h"
+#include <dprintf.h>
+
+void pm_debug_msg(com32sys_t *regs)
+{
+ (void)regs; /* For the non-DEBUG configuration */
+
+ dprintf("%s\n", MK_PTR(0, regs->eax.w[0]));
+}
diff --git a/core/diskboot.inc b/core/diskboot.inc
index 141986e8..89bdd968 100644
--- a/core/diskboot.inc
+++ b/core/diskboot.inc
@@ -28,6 +28,7 @@
; reduce the code size...
;
+ global StackBuf
StackBuf equ STACK_TOP-44-92 ; Start the stack here (grow down - 4K)
PartInfo equ StackBuf
.mbr equ PartInfo
@@ -102,7 +103,6 @@ superblock_len_fat32 equ $-superblock+54
zb 54 ; Maximum needed size
superblock_max equ $-superblock
- global SecPerClust
SecPerClust equ bxSecPerClust
;
@@ -384,7 +384,11 @@ getonesec_cbios:
;
; kaboom: write a message and bail out.
;
+%ifdef BINFMT
global kaboom
+%else
+ global kaboom:function hidden
+%endif
disk_error:
kaboom:
xor si,si
diff --git a/core/diskfs.inc b/core/diskfs.inc
index 41391e7f..d0f2804c 100644
--- a/core/diskfs.inc
+++ b/core/diskfs.inc
@@ -33,25 +33,6 @@ BS_MAGIC_VER equ 0x1b << 9
MIN_SECTOR_SHIFT equ 9
MIN_SECTOR_SIZE equ (1 << MIN_SECTOR_SHIFT)
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_appendlen: resw 1
-vk_type: resb 1 ; Type of file
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
-
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -60,7 +41,7 @@ vk_end: equ $ ; Should be <= vk_size
; Memory below this point is reserved for the BIOS and the MBR
;
section .earlybss
- global trackbuf
+ global trackbuf:data hidden
trackbufsize equ 8192
trackbuf resb trackbufsize ; Track buffer goes here
; ends at 2800h
@@ -70,7 +51,6 @@ trackbuf resb trackbufsize ; Track buffer goes here
;
%include "diskstart.inc"
-
;
; Now, everything is "up and running"... patch kaboom for more
; verbosity and using the full screen system
@@ -84,47 +64,59 @@ trackbuf resb trackbufsize ; Track buffer goes here
stosw
;
-; Now we're all set to start with our *real* business. First load the
-; configuration file (if any) and parse it.
+; If we get to this point ldlinux.c32 failed to run. There's nothing
+; left to do but inform that user that something went wrong.
;
-; In previous versions I avoided using 32-bit registers because of a
-; rumour some BIOSes clobbered the upper half of 32-bit registers at
-; random. I figure, though, that if there are any of those still left
-; they probably won't be trying to install Linux on them...
+enter_command:
+auto_boot:
+ jmp kaboom
+
+ section .bss16
+ alignb 4
+ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
+ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
+KernelExtPtr resw 1 ; During search, final null pointer
+FuncFlag resb 1 ; Escape sequences received from keyboard
+KernelType resb 1 ; Kernel type, from vkernel, if known
+ global KernelName
+KernelName resb FILENAME_MAX ; Mangled name for kernel
+
+ section .text16
;
-; The code is still ripe with 16-bitisms, though. Not worth the hassle
-; to take'm out. In fact, we may want to put them back if we're going
-; to boot ELKS at some point.
+; COMBOOT-loading code
;
+%include "comboot.inc"
+%include "com32.inc"
;
-; Load configuration file
+; Boot sector loading code
;
- pm_call pm_load_config
- jz no_config_file
;
-; Now we have the config file open. Parse the config file and
-; run the user interface.
+; Abort loading code
;
-%include "ui.inc"
;
+; Hardware cleanup common code
+;
+
+%include "localboot.inc"
+
;
; kaboom2: once everything is loaded, replace the part of kaboom
; starting with "kaboom.patch" with this part
kaboom2:
mov si,err_bootfailed
- call writestr
+ pm_call pm_writestr
cmp byte [kaboom.again+1],18h ; INT 18h version?
je .int18
- call getchar
- call vgaclearmode
+ pm_call pm_getchar
+ pm_call syslinux_force_text_mode
int 19h ; And try once more to boot...
.norge: jmp short .norge ; If int 19h returned; this is the end
.int18:
- call vgaclearmode
+ pm_call syslinux_force_text_mode
int 18h
.noreg: jmp short .noreg ; Nynorsk
@@ -133,16 +125,13 @@ kaboom2:
; -----------------------------------------------------------------------------
%include "common.inc" ; Universal modules
-%include "plaincon.inc" ; writechr
-%include "writestr.inc" ; String output
-%include "writehex.inc" ; Hexadecimal output
-%include "localboot.inc" ; Disk-based local boot
; -----------------------------------------------------------------------------
; Begin data section
; -----------------------------------------------------------------------------
section .data16
+ global copyright_str
copyright_str db ' Copyright (C) 1994-'
asciidec YEAR
db ' H. Peter Anvin et al', CR, LF, 0
@@ -150,25 +139,6 @@ err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
db 'a key to continue.', CR, LF, 0
;
-; Config file keyword table
-;
-%include "keywords.inc"
-
-;
-; Extensions to search for (in *forward* order).
-;
- alignz 4
-exten_table: db '.cbt' ; COMBOOT (specific)
-%if IS_SYSLINUX
- db '.bss' ; Boot sector (add superblock)
-%endif
- db '.bs', 0 ; Boot sector
- db '.com' ; COMBOOT (same as DOS)
- db '.c32' ; COM32
-exten_table_end:
- dd 0, 0 ; Need 8 null bytes here
-
-;
; Misc initialized (data) variables
;
%ifdef debug ; This code for debugging only
diff --git a/core/diskstart.inc b/core/diskstart.inc
index b2ef2b63..a2ede959 100644
--- a/core/diskstart.inc
+++ b/core/diskstart.inc
@@ -92,7 +92,7 @@ BannerPtr dw syslinux_banner - LDLINUX_SYS
; Base directory name and subvolume, if applicable.
;
%define HAVE_CURRENTDIRNAME
- global CurrentDirName, SubvolName
+ global CurrentDirName:data hidden, SubvolName:data hidden
CurrentDirName times CURRENTDIR_MAX db 0
SubvolName times SUBVOL_MAX db 0
@@ -142,6 +142,7 @@ print_bios:
call writestr_early
section .earlybss
+ global BIOSName
alignb 2
%define HAVE_BIOSNAME 1
BIOSName resw 1
@@ -231,7 +232,7 @@ verify_checksum:
;
; This routine assumes CS == DS.
;
- global getlinsec
+ global getlinsec:function hidden
getlinsec:
pushad
add eax,[Hidden] ; Add partition offset
@@ -504,6 +505,7 @@ expand_super:
mov di,[bsSecPerTrack]
movzx ebp,word [MaxTransfer]
pm_call fs_init
+ pm_call load_env32
popad
section .bss16
@@ -513,6 +515,7 @@ SuperInfo resq 16 ; The first 16 bytes expanded 8 times
; Banner information not needed in sector 1
;
section .data16
+ global syslinux_banner
syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR
late_banner db ' ', DATE_STR, 0
diff --git a/core/dmi.c b/core/dmi.c
new file mode 100644
index 00000000..9cbe2832
--- /dev/null
+++ b/core/dmi.c
@@ -0,0 +1,383 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Search DMI information for specific data or strings
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/bitops.h>
+#include <sys/cpu.h>
+#include <syslinux/sysappend.h>
+#include "core.h"
+
+struct dmi_table {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+};
+
+struct dmi_header {
+ char signature[5];
+ uint8_t csum;
+ uint16_t tbllen;
+ uint32_t tbladdr;
+ uint16_t nstruc;
+ uint8_t revision;
+ uint8_t reserved;
+};
+
+struct smbios_header {
+ char signature[4];
+ uint8_t csum;
+ uint8_t len;
+ uint8_t major;
+ uint8_t minor;
+ uint16_t maxsize;
+ uint8_t revision;
+ uint8_t fmt[5];
+
+ struct dmi_header dmi;
+};
+
+static const struct dmi_header *dmi;
+
+static uint8_t checksum(const void *buf, size_t len)
+{
+ const uint8_t *p = buf;
+ uint8_t csum = 0;
+
+ while (len--)
+ csum += *p++;
+
+ return csum;
+}
+
+static bool is_old_dmi(size_t dptr)
+{
+ const struct dmi_header *dmi = (void *)dptr;
+
+ return !memcmp(dmi->signature, "_DMI_", 5) &&
+ !checksum(dmi, 0x0f);
+ return false;
+}
+
+static bool is_smbios(size_t dptr)
+{
+ const struct smbios_header *smb = (void *)dptr;
+
+ return !memcmp(smb->signature, "_SM_", 4) &&
+ !checksum(smb, smb->len) &&
+ is_old_dmi(dptr+16);
+}
+
+/*
+ * Find the root structure
+ */
+static void dmi_find_header(void)
+{
+ size_t dptr;
+
+ /* Search for _SM_ or _DMI_ structure */
+ for (dptr = 0xf0000 ; dptr < 0x100000 ; dptr += 16) {
+ if (is_smbios(dptr)) {
+ dmi = (const struct dmi_header *)(dptr + 16);
+ break;
+ } else if (is_old_dmi(dptr)) {
+ dmi = (const struct dmi_header *)dptr;
+ break;
+ }
+ }
+}
+
+/*
+ * Return a specific data element in a specific table, and verify
+ * that it is within the bounds of the table.
+ */
+static const void *dmi_find_data(uint8_t type, uint8_t base, uint8_t length)
+{
+ const struct dmi_table *table;
+ size_t offset, end;
+ unsigned int tblcount;
+
+ if (!dmi)
+ return NULL;
+
+ if (base < 2)
+ return NULL;
+
+ end = base+length;
+
+ offset = 0;
+ tblcount = dmi->nstruc;
+
+ while (offset+6 <= dmi->tbllen && tblcount--) {
+ table = (const struct dmi_table *)(dmi->tbladdr + offset);
+
+ if (table->type == 127) /* End of table */
+ break;
+
+ if (table->length < sizeof *table)
+ break; /* Invalid length */
+
+ offset += table->length;
+
+ if (table->type == type && end <= table->length)
+ return (const char *)table + base;
+
+ /* Search for a double NUL terminating the string table */
+ while (offset+2 <= dmi->tbllen &&
+ *(const uint16_t *)(dmi->tbladdr + offset) != 0)
+ offset++;
+
+ offset += 2;
+ }
+
+ return NULL;
+}
+
+/*
+ * Return a specific string in a specific table.
+ */
+static const char *dmi_find_string(uint8_t type, uint8_t base)
+{
+ const struct dmi_table *table;
+ size_t offset;
+ unsigned int tblcount;
+
+ if (!dmi)
+ return NULL;
+
+ if (base < 2)
+ return NULL;
+
+ offset = 0;
+ tblcount = dmi->nstruc;
+
+ while (offset+6 <= dmi->tbllen && tblcount--) {
+ table = (const struct dmi_table *)(dmi->tbladdr + offset);
+
+ if (table->type == 127) /* End of table */
+ break;
+
+ if (table->length < sizeof *table)
+ break; /* Invalid length */
+
+ offset += table->length;
+
+ if (table->type == type && base < table->length) {
+ uint8_t index = ((const uint8_t *)table)[base];
+ const char *p = (const char *)table + table->length;
+ const char *str;
+ char c;
+
+ if (!index)
+ return NULL; /* String not present */
+
+ while (--index) {
+ if (!*p)
+ return NULL;
+
+ do {
+ if (offset++ >= dmi->tbllen)
+ return NULL;
+ c = *p++;
+ } while (c);
+ }
+
+ /* Make sure the string is null-terminated */
+ str = p;
+ do {
+ if (offset++ >= dmi->tbllen)
+ return NULL;
+ c = *p++;
+ } while (c);
+ return str;
+ }
+
+ /* Search for a double NUL terminating the string table */
+ while (offset+2 <= dmi->tbllen &&
+ *(const uint16_t *)(dmi->tbladdr + offset) != 0)
+ offset++;
+
+ offset += 2;
+ }
+
+ return NULL;
+}
+
+struct sysappend_dmi_strings {
+ const char *prefix;
+ enum syslinux_sysappend sa;
+ uint8_t index;
+ uint8_t offset;
+};
+
+static const struct sysappend_dmi_strings dmi_strings[] = {
+ { "SYSVENDOR=", SYSAPPEND_SYSVENDOR, 1, 0x04 },
+ { "SYSPRODUCT=", SYSAPPEND_SYSPRODUCT, 1, 0x05 },
+ { "SYSVERSION=", SYSAPPEND_SYSVERSION, 1, 0x06 },
+ { "SYSSERIAL=", SYSAPPEND_SYSSERIAL, 1, 0x07 },
+ { "SYSSKU=", SYSAPPEND_SYSSKU, 1, 0x19 },
+ { "SYSFAMILY=", SYSAPPEND_SYSFAMILY, 1, 0x1a },
+ { "MBVENDOR=", SYSAPPEND_MBVENDOR, 2, 0x04 },
+ { "MBPRODUCT=", SYSAPPEND_MBPRODUCT, 2, 0x05 },
+ { "MBVERSION=", SYSAPPEND_MBVERSION, 2, 0x06 },
+ { "MBSERIAL=", SYSAPPEND_MBSERIAL, 2, 0x07 },
+ { "MBASSET=", SYSAPPEND_MBASSET, 2, 0x08 },
+ { "BIOSVENDOR=", SYSAPPEND_BIOSVENDOR, 0, 0x04 },
+ { "BIOSVERSION=", SYSAPPEND_BIOSVERSION, 0, 0x05 },
+ { NULL, 0, 0, 0 }
+};
+
+/*
+ * Install the string in the string table, if nonempty, after
+ * removing leading and trailing whitespace.
+ */
+static bool is_ctl_or_whitespace(char c)
+{
+ return (c <= ' ' || c == '\x7f');
+}
+
+static const char *dmi_install_string(const char *pfx, const char *str)
+{
+ const char *p, *ep;
+ size_t pfxlen;
+ char *nstr, *q;
+
+ if (!str)
+ return NULL;
+
+ while (*str && is_ctl_or_whitespace(*str))
+ str++;
+
+ if (!*str)
+ return NULL;
+
+ ep = p = str;
+ while (*p) {
+ if (!is_ctl_or_whitespace(*p))
+ ep = p+1;
+ p++;
+ }
+
+ pfxlen = strlen(pfx);
+ q = nstr = malloc(pfxlen + (ep-str) + 1);
+ if (!nstr)
+ return NULL;
+ memcpy(q, pfx, pfxlen);
+ q += pfxlen;
+ memcpy(q, str, ep-str);
+ q += (ep-str);
+ *q = '\0';
+
+ return nstr;
+}
+
+static void sysappend_set_sysff(const uint8_t *type)
+{
+ static char sysff_str[] = "SYSFF=000";
+
+ if (!type || !*type)
+ return;
+
+ sprintf(sysff_str+6, "%u", *type & 0x7f);
+ sysappend_strings[SYSAPPEND_SYSFF] = sysff_str;
+}
+
+struct cpuflag {
+ uint8_t bit;
+ char flag;
+};
+
+static void sysappend_set_cpu(void)
+{
+ static char cpu_str[6+6] = "CPU=";
+ char *p = cpu_str + 4;
+ static const struct cpuflag cpuflags[] = {
+ { 0*32+ 6, 'P' }, /* PAE */
+ { 1*32+ 5, 'V' }, /* VMX */
+ { 1*32+ 6, 'T' }, /* SMX (TXT) */
+ { 2*32+20, 'X' }, /* XD/NX */
+ { 2*32+29, 'L' }, /* Long mode (x86-64) */
+ { 3*32+ 2, 'S' }, /* SVM */
+ { 0, 0 }
+ };
+ const struct cpuflag *cf;
+
+ /* Not technically from DMI, but it fit here... */
+
+ if (!cpu_has_eflag(EFLAGS_ID)) {
+ /* No CPUID */
+ *p++ = cpu_has_eflag(EFLAGS_AC) ? '4' : '3';
+ } else {
+ uint32_t flags[4], eax, ebx, family;
+ uint32_t ext_level;
+
+ cpuid(1, &eax, &ebx, &flags[1], &flags[0]);
+ family = (eax & 0x0ff00f00) >> 8;
+ *p++ = family >= 6 ? '6' : family + '0';
+
+ ext_level = cpuid_eax(0x80000000);
+ if (ext_level >= 0x80000001 && ext_level <= 0x8000ffff) {
+ cpuid(0x80000001, &eax, &ebx, &flags[3], &flags[2]);
+ } else {
+ flags[2] = flags[3] = 0;
+ }
+
+ for (cf = cpuflags; cf->flag; cf++) {
+ if (test_bit(cf->bit, flags))
+ *p++ = cf->flag;
+ }
+ }
+
+ *p = '\0';
+
+ sysappend_strings[SYSAPPEND_CPU] = cpu_str;
+}
+
+void dmi_init(void)
+{
+ const struct sysappend_dmi_strings *ds;
+
+ sysappend_set_cpu();
+
+ dmi_find_header();
+ if (!dmi)
+ return;
+
+ sysappend_set_uuid(dmi_find_data(1, 0x08, 16));
+ sysappend_set_sysff(dmi_find_data(3, 0x05, 1));
+
+ for (ds = dmi_strings; ds->prefix; ds++) {
+ if (!sysappend_strings[ds->sa]) {
+ const char *str = dmi_find_string(ds->index, ds->offset);
+ sysappend_strings[ds->sa] = dmi_install_string(ds->prefix, str);
+ }
+ }
+}
diff --git a/core/elflink/common.h b/core/elflink/common.h
new file mode 100644
index 00000000..e288d1e4
--- /dev/null
+++ b/core/elflink/common.h
@@ -0,0 +1,62 @@
+/*
+ * common.h - Common internal operations performed by the module subsystem
+ *
+ * Created on: Aug 11, 2008
+ * Author: Stefan Bucur <stefanb@zytor.com>
+ */
+
+#ifndef COMMON_H_
+#define COMMON_H_
+
+#include <stdio.h>
+
+#include <sys/module.h>
+#include <linux/list.h>
+
+#include "elfutils.h"
+
+// Performs an operation and jumps to a given label if an error occurs
+#define CHECKED(res, expr, error) \
+ do { \
+ (res) = (expr); \
+ if ((res) < 0) \
+ goto error; \
+ } while (0)
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+//#define ELF_DEBUG
+
+#ifdef ELF_DEBUG
+#define DBG_PRINT(fmt, args...) fprintf(stderr, "[ELF] " fmt, ##args)
+#else
+#define DBG_PRINT(fmt, args...) // Expand to nothing
+#endif
+
+// User-space debugging routines
+#ifdef ELF_DEBUG
+extern void print_elf_ehdr(Elf32_Ehdr * ehdr);
+extern void print_elf_symbols(struct elf_module *module);
+#endif //ELF_DEBUG
+
+/*
+ * Image files manipulation routines
+ */
+
+extern int image_load(struct elf_module *module);
+extern int image_unload(struct elf_module *module);
+extern int image_read(void *buff, size_t size, struct elf_module *module);
+extern int image_skip(size_t size, struct elf_module *module);
+extern int image_seek(Elf32_Off offset, struct elf_module *module);
+
+extern struct module_dep *module_dep_alloc(struct elf_module *module);
+
+extern int check_header_common(Elf32_Ehdr * elf_hdr);
+
+extern int enforce_dependency(struct elf_module *req, struct elf_module *dep);
+extern int clear_dependency(struct elf_module *req, struct elf_module *dep);
+
+extern int check_symbols(struct elf_module *module);
+
+#endif /* COMMON_H_ */
diff --git a/core/elflink/elfutils.h b/core/elflink/elfutils.h
new file mode 100644
index 00000000..3c8e70fc
--- /dev/null
+++ b/core/elflink/elfutils.h
@@ -0,0 +1,67 @@
+#ifndef ELF_UTILS_H_
+#define ELF_UTILS_H_
+
+#include <elf.h>
+#include <stdlib.h>
+
+/**
+ * elf_get_header - Returns a pointer to the ELF header structure.
+ * @elf_image: pointer to the ELF file image in memory
+ */
+static inline Elf32_Ehdr *elf_get_header(void *elf_image)
+{
+ return (Elf32_Ehdr *) elf_image;
+}
+
+/**
+ * elf_get_pht - Returns a pointer to the first entry in the PHT.
+ * @elf_image: pointer to the ELF file image in memory
+ */
+static inline Elf32_Phdr *elf_get_pht(void *elf_image)
+{
+ Elf32_Ehdr *elf_hdr = elf_get_header(elf_image);
+
+ return (Elf32_Phdr *) ((Elf32_Off) elf_hdr + elf_hdr->e_phoff);
+}
+
+//
+/**
+ * elf_get_ph - Returns the element with the given index in the PTH
+ * @elf_image: pointer to the ELF file image in memory
+ * @index: the index of the PHT entry to look for
+ */
+static inline Elf32_Phdr *elf_get_ph(void *elf_image, int index)
+{
+ Elf32_Phdr *elf_pht = elf_get_pht(elf_image);
+ Elf32_Ehdr *elf_hdr = elf_get_header(elf_image);
+
+ return (Elf32_Phdr *) ((Elf32_Off) elf_pht + index * elf_hdr->e_phentsize);
+}
+
+/**
+ * elf_hash - Returns the index in a SysV hash table for the symbol name.
+ * @name: the name of the symbol to look for
+ */
+extern unsigned long elf_hash(const unsigned char *name);
+
+/**
+ * elf_gnu_hash - Returns the index in a GNU hash table for the symbol name.
+ * @name: the name of the symbol to look for
+ */
+extern unsigned long elf_gnu_hash(const unsigned char *name);
+
+/**
+ * elf_malloc - Allocates memory to be used by ELF module contents.
+ * @memptr: pointer to a variable to hold the address of the allocated block.
+ * @alignment: alignment constraints of the block
+ * @size: the required size of the block
+ */
+extern int elf_malloc(void **memptr, size_t alignment, size_t size);
+
+/**
+ * elf_free - Releases memory previously allocated by elf_malloc.
+ * @memptr: the address of the allocated block
+ */
+extern void elf_free(void *memptr);
+
+#endif /*ELF_UTILS_H_ */
diff --git a/core/elflink/load_env32.c b/core/elflink/load_env32.c
new file mode 100644
index 00000000..8551831f
--- /dev/null
+++ b/core/elflink/load_env32.c
@@ -0,0 +1,249 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <console.h>
+#include <dprintf.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <syslinux/config.h>
+#include <setjmp.h>
+#include <linux/list.h>
+#include <netinet/in.h>
+#include <sys/cpu.h>
+#include <core.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <fs.h>
+#include <ctype.h>
+#include <alloca.h>
+
+#include <sys/exec.h>
+#include <sys/module.h>
+#include "common.h"
+
+#define LDLINUX "ldlinux.c32"
+
+extern char __dynstr_start[];
+extern char __dynstr_len[], __dynsym_len[];
+extern char __dynsym_start[];
+extern char __got_start[];
+extern Elf32_Dyn __dynamic_start[];
+extern Elf32_Word __gnu_hash_start[];
+
+struct elf_module core_module = {
+ .name = "(core)",
+ .shallow = true,
+ .required = LIST_HEAD_INIT((core_module.required)),
+ .dependants = LIST_HEAD_INIT((core_module.dependants)),
+ .list = LIST_HEAD_INIT((core_module.list)),
+ .module_addr = (void *)0x0,
+ .base_addr = (Elf32_Addr) 0x0,
+ .ghash_table = __gnu_hash_start,
+ .str_table = __dynstr_start,
+ .sym_table = __dynsym_start,
+ .got = __got_start,
+ .dyn_table = __dynamic_start,
+ .strtable_size = (size_t) __dynstr_len,
+ .syment_size = sizeof(Elf32_Sym),
+ .symtable_size = (size_t) __dynsym_len
+};
+
+/*
+ * Initializes the module subsystem by taking the core module
+ * (preinitialized shallow module) and placing it on top of the
+ * modules_head_list.
+ */
+void init_module_subsystem(struct elf_module *module)
+{
+ list_add(&module->list, &modules_head);
+}
+
+__export int start_ldlinux(int argc, char **argv)
+{
+ int rv;
+
+again:
+ rv = spawn_load(LDLINUX, argc, argv);
+ if (rv == EEXIST) {
+ /*
+ * If a COM32 module calls execute() we may need to
+ * unload all the modules loaded since ldlinux.c32,
+ * and restart initialisation. This is especially
+ * important for config files.
+ *
+ * But before we do that, try our best to make sure
+ * that spawn_load() is gonna succeed, e.g. that we
+ * can find LDLINUX it in PATH.
+ */
+ struct elf_module *ldlinux;
+ FILE *f;
+
+ f = findpath(LDLINUX);
+ if (!f)
+ return ENOENT;
+
+ fclose(f);
+ ldlinux = unload_modules_since(LDLINUX);
+
+ /*
+ * Finally unload LDLINUX.
+ *
+ * We'll reload it when we jump to 'again' which will
+ * cause all the initialsation steps to be executed
+ * again.
+ */
+ module_unload(ldlinux);
+ goto again;
+ }
+
+ return rv;
+}
+
+/* note to self: do _*NOT*_ use static key word on this function */
+void load_env32(com32sys_t * regs __unused)
+{
+ struct file_info *fp;
+ int fd;
+ char *argv[] = { LDLINUX, NULL };
+ char realname[FILENAME_MAX];
+
+ static const char *search_directories[] = {
+ "/boot/isolinux",
+ "/isolinux",
+ "/boot/syslinux",
+ "/syslinux",
+ "/",
+ NULL
+ };
+
+ static const char *filenames[] = {
+ LDLINUX,
+ NULL
+ };
+
+ dprintf("Starting 32 bit elf module subsystem...\n");
+
+ if (strlen(CurrentDirName) && !path_add(CurrentDirName)) {
+ printf("Couldn't allocate memory for PATH\n");
+ goto out;
+ }
+
+ init_module_subsystem(&core_module);
+
+ start_ldlinux(1, argv);
+
+ /*
+ * If we failed to load LDLINUX it could be because our
+ * current working directory isn't the install directory. Try
+ * a bit harder to find LDLINUX. If search_dirs() succeeds
+ * in finding LDLINUX it will set the cwd.
+ */
+ fd = opendev(&__file_dev, NULL, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ fp = &__file_info[fd];
+
+ if (!search_dirs(&fp->i.fd, search_directories, filenames, realname)) {
+ char path[FILENAME_MAX];
+
+ /*
+ * search_dirs() sets the current working directory if
+ * it successfully opens the file. Add the directory
+ * in which we found ldlinux.c32 to PATH.
+ */
+ if (!core_getcwd(path, sizeof(path)))
+ goto out;
+
+ if (!path_add(path)) {
+ printf("Couldn't allocate memory for PATH\n");
+ goto out;
+ }
+
+ start_ldlinux(1, argv);
+ }
+
+out:
+ writestr("\nFailed to load ldlinux.c32");
+}
+
+static const char *__cmdline;
+__export const char *com32_cmdline(void)
+{
+ return __cmdline;
+}
+
+__export int create_args_and_load(char *cmdline)
+{
+ char *p, **argv;
+ int argc;
+ int i;
+
+ if (!cmdline)
+ return -1;
+
+ for (argc = 0, p = cmdline; *p; argc++) {
+ /* Find the end of this arg */
+ while(*p && !isspace(*p))
+ p++;
+
+ /*
+ * Now skip all whitespace between arguments.
+ */
+ while (*p && isspace(*p))
+ p++;
+ }
+
+ /*
+ * Generate a copy of argv on the stack as this is
+ * traditionally where process arguments go.
+ *
+ * argv[0] must be the command name. Remember to allocate
+ * space for the sentinel NULL.
+ */
+ argv = alloca((argc + 1) * sizeof(char *));
+
+ for (i = 0, p = cmdline; i < argc; i++) {
+ char *start;
+ int len = 0;
+
+ start = p;
+
+ /* Find the end of this arg */
+ while(*p && !isspace(*p)) {
+ p++;
+ len++;
+ }
+
+ argv[i] = malloc(len + 1);
+ strncpy(argv[i], start, len);
+ argv[i][len] = '\0';
+
+ /*
+ * Now skip all whitespace between arguments.
+ */
+ while (*p && isspace(*p))
+ p++;
+
+ /*
+ * Point __cmdline at "argv[1] ... argv[argc-1]"
+ */
+ if (i == 0)
+ __cmdline = p;
+ }
+
+ /* NUL-terminate */
+ argv[argc] = NULL;
+
+ return spawn_load(argv[0], argc, argv);
+}
+
+void pm_env32_run(com32sys_t *regs)
+{
+ char *cmdline;
+
+ cmdline = MK_PTR(regs->es, regs->ebx.w[0]);
+ if (create_args_and_load(cmdline) < 0)
+ printf("Failed to run com32 module\n");
+}
diff --git a/core/errno.c b/core/errno.c
new file mode 100644
index 00000000..adfd92f8
--- /dev/null
+++ b/core/errno.c
@@ -0,0 +1,4 @@
+#include <klibc/compiler.h>
+#include <errno.h>
+
+__export int errno;
diff --git a/core/extern.inc b/core/extern.inc
index 816659b7..0d6a391b 100644
--- a/core/extern.inc
+++ b/core/extern.inc
@@ -9,6 +9,26 @@
; rllpack.c
extern rllpack, rllunpack
+ ; hello.c
+ extern hello
+
+ ;abort.c
+ extern abort_load_new
+
+ ; elflink/load_env32.c
+ extern load_env32, pm_env32_run
+
+ ; memscan.c
+ extern highmem_init
+
+ extern linux_kernel
+
+ extern mp1, mp2, mp3, mp4, mp5
+
+ extern hexdump, mydump
+
+ extern mem_init
+
; fs.c
extern fs_init, pm_searchdir, getfssec, getfsbytes
extern pm_mangle_name, pm_load_config
@@ -24,9 +44,51 @@
; newconfig.c
extern pm_is_config_file
+%ifdef DEBUG
+ ; debug.c
+ extern pm_debug_msg
+
+ %macro dprint 1+
+ push ax
+ call %%fwd
+ db %1
+ db 0
+%%fwd: pop ax
+ pm_call pm_debug_msg
+ pop ax
+ %endmacro
+%else
+ %macro dprint 1+
+ %endmacro
+%endif
+
%if IS_PXELINUX
; pxe.c
- extern unload_pxe, reset_pxe
+ extern unload_pxe, reset_pxe, http_bake_cookies
%endif
+ ; plaincon.c
+ extern pm_writechr
+
+ ; cleanup.c
+ extern cleanup_hardware
+
+ ; writestr.c
+ extern pm_writestr, crlf
+
+ ; writehex.c
+ extern pm_writehex2, pm_writehex4, pm_writehex8
+
+ ; graphics.c
+ extern syslinux_force_text_mode, vgashowcursor, vgahidecursor, pm_using_vga
+
+ ; conio.c
+ extern pm_pollchar, pm_write_serial, pm_serialcfg
+
+ ; font.c
+ extern pm_getchar, pm_adjust_screen, pm_userfont
+
+ ; localboot.c
+ extern pm_local_boot
+
%endif ; EXTERN_INC
diff --git a/core/font.c b/core/font.c
new file mode 100644
index 00000000..81eb90ce
--- /dev/null
+++ b/core/font.c
@@ -0,0 +1,181 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ *
+ * font.c
+ *
+ * VGA font handling code
+ *
+ */
+
+#include <sys/io.h>
+#include <stdio.h>
+#include <fs.h>
+
+#include "bios.h"
+#include "graphics.h"
+#include "core.h"
+
+__export uint8_t UserFont = 0; /* Using a user-specified font */
+
+__export __lowmem char fontbuf[8192];
+
+uint16_t GXPixCols = 1; /* Graphics mode pixel columns */
+uint16_t GXPixRows = 1; /* Graphics mode pixel rows */
+
+/*
+ * loadfont: Load a .psf font file and install it onto the VGA console
+ * (if we're not on a VGA screen then ignore.)
+ */
+__export void loadfont(const char *filename)
+{
+ struct psfheader {
+ uint16_t magic;
+ uint8_t mode;
+ uint8_t height;
+ } hdr;
+ FILE *f;
+
+ f = fopen(filename, "r");
+ if (!f)
+ return;
+
+ /* Read header */
+ if (_fread(&hdr, sizeof hdr, f) != sizeof hdr)
+ goto fail;
+
+ /* Magic number */
+ if (hdr.magic != 0x0436)
+ goto fail;
+
+ /* File mode: font modes 0-5 supported */
+ if (hdr.mode > 5)
+ goto fail;
+
+ /* VGA minimum/maximum */
+ if (hdr.height < 2 || hdr.height > 32)
+ goto fail;
+
+ /* Load the actual font into the font buffer. */
+ memset(fontbuf, 0, 256*32);
+ if (_fread(fontbuf, 256*hdr.height, f) != 256*hdr.height)
+ goto fail;
+
+ /* Loaded OK */
+ VGAFontSize = hdr.height;
+ UserFont = 1; /* Set font flag */
+ use_font();
+
+fail:
+ fclose(f);
+}
+
+/*
+ * use_font:
+ * This routine activates whatever font happens to be in the
+ * vgafontbuf, and updates the adjust_screen data.
+ * Must be called with CS = DS
+ */
+void use_font(void)
+{
+ com32sys_t ireg, oreg;
+ uint8_t bytes = VGAFontSize;
+
+ /* Nonstandard mode? */
+ if (UsingVGA & ~0x3)
+ syslinux_force_text_mode();
+
+ memset(&ireg, 0, sizeof(ireg));
+
+ ireg.es = SEG(fontbuf);
+ ireg.ebp.w[0] = OFFS(fontbuf); /* ES:BP -> font */
+
+ /* Are we using a user-specified font? */
+ if (UserFont & 0x1) {
+ /* Are we in graphics mode? */
+ if (UsingVGA & 0x1) {
+ uint8_t rows;
+
+ rows = GXPixRows / bytes;
+ VidRows = rows - 1;
+
+ /* Set user character table */
+ ireg.eax.w[0] = 0x1121;
+ ireg.ebx.b[0] = 0;
+ ireg.ecx.b[0] = bytes; /* bytes/character */
+ ireg.edx.b[0] = rows;
+
+ __intcall(0x10, &ireg, &oreg);
+
+ /* 8 pixels/character */
+ VidCols = ((GXPixCols >> 3) - 1);
+
+ /* No need to call adjust_screen */
+ return;
+ } else {
+ ireg.eax.w[0] = 0x1110; /* Load into VGA RAM */
+ ireg.ebx.b[0] = 0;
+ ireg.ebx.b[1] = bytes; /* bytes/character */
+ ireg.ecx.w[0] = 256;
+ ireg.edx.w[0] = 0;
+
+ __intcall(0x10, &ireg, &oreg);
+
+ ireg.ebx.b[0] = 0;
+ ireg.eax.w[0] = 0x1103; /* Select page 0 */
+ __intcall(0x10, &ireg, NULL);
+ }
+ }
+
+ adjust_screen();
+}
+
+/*
+ * adjust_screen: Set the internal variables associated with the screen size.
+ * This is a subroutine in case we're loading a custom font.
+ */
+void adjust_screen(void)
+{
+ com32sys_t ireg, oreg;
+ volatile uint8_t *vidrows = (volatile uint8_t *)BIOS_vidrows;
+ uint8_t rows, cols;
+
+ rows = *vidrows;
+ if (!rows) {
+ /*
+ * No vidrows in BIOS, assume 25.
+ * (Remember: vidrows == rows-1)
+ */
+ rows = 24;
+ }
+
+ VidRows = rows;
+
+ ireg.eax.b[1] = 0x0f; /* Read video state */
+ __intcall(0x10, &ireg, &oreg);
+ cols = oreg.eax.b[1];
+
+ VidCols = --cols; /* Store count-1 (same as rows) */
+}
+
+void pm_adjust_screen(com32sys_t *regs __unused)
+{
+ adjust_screen();
+}
+
+void pm_userfont(com32sys_t *regs)
+{
+ regs->es = SEG(fontbuf);
+ regs->ebx.w[0] = OFFS(fontbuf);
+}
diff --git a/core/font.inc b/core/font.inc
deleted file mode 100644
index 12236358..00000000
--- a/core/font.inc
+++ /dev/null
@@ -1,152 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; font.inc
-;;
-;; VGA font handling code
-;;
-
- section .text16
-
-;
-; loadfont: Load a .psf font file and install it onto the VGA console
-; (if we're not on a VGA screen then ignore.)
-; The font is on top of the getc stack.
-;
-loadfont.err: jmp close ; Tailcall the close routine
-
-loadfont:
- mov di,trackbuf
- mov cx,4
- call readc ; Read header
- jc .err
-
- mov ax,[trackbuf] ; Magic number
- cmp ax,0436h
- jne .err
-
- mov al,[trackbuf+2] ; File mode
- cmp al,5 ; Font modes 0-5 supported
- ja .err
-
- xor bx,bx
- mov bh,[trackbuf+3] ; Height of font
- cmp bh,2 ; VGA minimum
- jb .err
- cmp bh,32 ; VGA maximum
- ja .err
-
- ; Load the actual font
- mov di,trackbuf
- mov cx,bx ; Bytes = font height * 256
- call readc
- jc .err
-
- call close
-
- ; Copy to font buffer
- mov si,trackbuf ; Start of font data
- mov [VGAFontSize],bh
- push es
- mov cx,aux_seg
- mov es,cx
- mov di,aux.fontbuf
- mov cx,bx
- shr cx,2
- rep movsd
- pop es
-
- mov [UserFont], byte 1 ; Set font flag
-
-;
-; use_font:
-; This routine activates whatever font happens to be in the
-; vgafontbuf, and updates the adjust_screen data.
-; Must be called with CS = DS
-;
-use_font:
- test byte [UsingVGA], ~03h ; Nonstandard mode?
- jz .modeok
- call vgaclearmode
-
-.modeok:
- test [UserFont], byte 1 ; Are we using a user-specified font?
- jz adjust_screen ; If not, just do the normal stuff
-
- push es
- mov bp,aux_seg
- mov es,bp
-
- mov bp,aux.fontbuf ; ES:BP -> font
- mov bh,[VGAFontSize]
- xor bl,bl ; Needed by both INT 10h calls
-
- test byte [UsingVGA], 01h ; Are we in graphics mode?
- jz .text
-
-.graphics:
- xor cx,cx
- mov cl,bh ; CX = bytes/character
- mov ax,[GXPixRows]
- div cl ; Compute char rows per screen
- mov dl,al
- dec ax
- mov [VidRows],al
- mov ax,1121h ; Set user character table
- int 10h
- mov ax,[GXPixCols]
- shr ax,3 ; 8 pixels/character
- dec ax
- mov [VidCols],al
- pop es
- ret ; No need to call adjust_screen
-
-.text:
- mov cx,256
- xor dx,dx
- mov ax,1110h
- int 10h ; Load into VGA RAM
- pop es
-
- xor bl,bl
- mov ax,1103h ; Select page 0
- int 10h
-
-;
-; adjust_screen: Set the internal variables associated with the screen size.
-; This is a subroutine in case we're loading a custom font.
-;
-adjust_screen:
- pusha
- mov al,[BIOS_vidrows]
- and al,al
- jnz vidrows_ok
- mov al,24 ; No vidrows in BIOS, assume 25
- ; (Remember: vidrows == rows-1)
-vidrows_ok: mov [VidRows],al
- mov ah,0fh
- int 10h ; Read video state
- dec ah ; Store count-1 (same as rows)
- mov [VidCols],ah
- popa
- ret
-
- section .data16
- alignz 2
-VGAFontSize dw 16 ; Defaults to 16 byte font
-UserFont db 0 ; Using a user-specified font
-
- section .bss16
- alignb 4
-GXPixCols resw 1 ; Graphics mode pixel columns
-GXPixRows resw 1 ; Graphics mode pixel rows
diff --git a/core/fs/btrfs/btrfs.c b/core/fs/btrfs/btrfs.c
index aeb7614a..16386cc0 100644
--- a/core/fs/btrfs/btrfs.c
+++ b/core/fs/btrfs/btrfs.c
@@ -673,5 +673,6 @@ const struct fs_ops btrfs_fs_ops = {
.mangle_name = generic_mangle_name,
.next_extent = btrfs_next_extent,
.readdir = btrfs_readdir,
- .load_config = generic_load_config
+ .chdir_start = generic_chdir_start,
+ .open_config = generic_open_config
};
diff --git a/core/fs/chdir.c b/core/fs/chdir.c
index 903cabce..276ea11c 100644
--- a/core/fs/chdir.c
+++ b/core/fs/chdir.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <string.h>
#include <dprintf.h>
+#include <fcntl.h>
#include "fs.h"
#include "cache.h"
@@ -54,7 +55,7 @@ static size_t generic_inode_to_path(struct inode *inode, char *dst, size_t bufsi
return s;
}
-size_t realpath(char *dst, const char *src, size_t bufsize)
+__export size_t realpath(char *dst, const char *src, size_t bufsize)
{
int rv;
struct file *file;
@@ -65,7 +66,7 @@ size_t realpath(char *dst, const char *src, size_t bufsize)
if (this_fs->fs_ops->realpath) {
s = this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
} else {
- rv = searchdir(src);
+ rv = searchdir(src, O_RDONLY);
if (rv < 0) {
dprintf("realpath: searchpath failure\n");
return -1;
@@ -83,7 +84,7 @@ size_t realpath(char *dst, const char *src, size_t bufsize)
return s;
}
-int chdir(const char *src)
+__export int chdir(const char *src)
{
int rv;
struct file *file;
@@ -97,7 +98,7 @@ int chdir(const char *src)
return this_fs->fs_ops->chdir(this_fs, src);
/* Otherwise it is a "conventional filesystem" */
- rv = searchdir(src);
+ rv = searchdir(src, O_RDONLY|O_DIRECTORY);
if (rv < 0)
return rv;
diff --git a/core/fs/diskio.c b/core/fs/diskio.c
index 66838161..60defd3e 100644
--- a/core/fs/diskio.c
+++ b/core/fs/diskio.c
@@ -7,6 +7,7 @@
#include <fs.h>
#include <disk.h>
#include <ilog2.h>
+#include <minmax.h>
#define RETRY_COUNT 6
@@ -396,24 +397,20 @@ struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start,
return &disk;
}
-
/*
* Initialize the device structure.
- *
- * NOTE: the disk cache needs to be revamped to support multiple devices...
*/
struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
uint16_t bsHeads, uint16_t bsSecPerTrack,
uint32_t MaxTransfer)
{
static struct device dev;
- static __hugebss char diskcache[128*1024];
dev.disk = disk_init(devno, cdrom, part_start,
bsHeads, bsSecPerTrack, MaxTransfer);
- dev.cache_data = diskcache;
- dev.cache_size = sizeof diskcache;
+ dev.cache_size = 128*1024;
+ dev.cache_data = malloc(dev.cache_size);
return &dev;
}
diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c
index 7988faaf..957c60b7 100644
--- a/core/fs/ext2/ext2.c
+++ b/core/fs/ext2/ext2.c
@@ -329,7 +329,8 @@ const struct fs_ops ext2_fs_ops = {
.getfssec = generic_getfssec,
.close_file = generic_close_file,
.mangle_name = generic_mangle_name,
- .load_config = generic_load_config,
+ .chdir_start = generic_chdir_start,
+ .open_config = generic_open_config,
.iget_root = ext2_iget_root,
.iget = ext2_iget,
.readlink = ext2_readlink,
diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c
index b08923cf..b2c20ee0 100644
--- a/core/fs/fat/fat.c
+++ b/core/fs/fat/fat.c
@@ -782,6 +782,34 @@ static int vfat_fs_init(struct fs_info *fs)
return fs->block_shift;
}
+static int vfat_copy_superblock(void *buf)
+{
+ struct fat_bpb fat;
+ struct disk *disk;
+ size_t sb_off;
+ void *dst;
+ int sb_len;
+
+ disk = this_fs->fs_dev->disk;
+ disk->rdwr_sectors(disk, &fat, 0, 1, 0);
+
+ /* XXX: Find better sanity checks... */
+ if (!fat.bxResSectors || !fat.bxFATs)
+ return -1;
+
+ sb_off = offsetof(struct fat_bpb, sector_size);
+ sb_len = offsetof(struct fat_bpb, fat12_16) - sb_off \
+ + sizeof(fat.fat12_16);
+
+ /*
+ * Only copy fields of the superblock we actually care about.
+ */
+ dst = buf + sb_off;
+ memcpy(dst, (void *)&fat + sb_off, sb_len);
+
+ return 0;
+}
+
const struct fs_ops vfat_fs_ops = {
.fs_name = "vfat",
.fs_flags = FS_USEMEM | FS_THISIND,
@@ -790,9 +818,11 @@ const struct fs_ops vfat_fs_ops = {
.getfssec = generic_getfssec,
.close_file = generic_close_file,
.mangle_name = vfat_mangle_name,
- .load_config = generic_load_config,
+ .chdir_start = generic_chdir_start,
+ .open_config = generic_open_config,
.readdir = vfat_readdir,
.iget_root = vfat_iget_root,
.iget = vfat_iget,
.next_extent = fat_next_extent,
+ .copy_super = vfat_copy_superblock,
};
diff --git a/core/fs/fs.c b/core/fs/fs.c
index 3cb27b0d..b6ee19c2 100644
--- a/core/fs/fs.c
+++ b/core/fs/fs.c
@@ -1,15 +1,20 @@
+#include <sys/file.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <dprintf.h>
+#include "core.h"
+#include "dev.h"
#include "fs.h"
#include "cache.h"
/* The currently mounted filesystem */
-struct fs_info *this_fs = NULL; /* Root filesystem */
+__export struct fs_info *this_fs = NULL; /* Root filesystem */
/* Actual file structures (we don't have malloc yet...) */
-struct file files[MAX_OPEN];
+__export struct file files[MAX_OPEN];
/* Symlink hard limits */
#define MAX_SYMLINK_CNT 20
@@ -34,9 +39,13 @@ struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data)
*/
void put_inode(struct inode *inode)
{
- while (inode && --inode->refcnt == 0) {
+ while (inode) {
struct inode *dead = inode;
- inode = inode->parent;
+ int refcnt = --(dead->refcnt);
+ dprintf("put_inode %p name %s refcnt %u\n", dead, dead->name, refcnt);
+ if (refcnt)
+ break; /* We still have references */
+ inode = dead->parent;
if (dead->name)
free((char *)dead->name);
free(dead);
@@ -68,7 +77,7 @@ static inline void free_file(struct file *file)
memset(file, 0, sizeof *file);
}
-void _close_file(struct file *file)
+__export void _close_file(struct file *file)
{
if (file->fs)
file->fs->fs_ops->close_file(file);
@@ -76,92 +85,37 @@ void _close_file(struct file *file)
}
/*
- * Convert between a 16-bit file handle and a file structure
+ * Find and open the configuration file
*/
-
-void pm_load_config(com32sys_t *regs)
+__export int open_config(void)
{
- int err;
+ int fd, handle;
+ struct file_info *fp;
- err = this_fs->fs_ops->load_config();
+ fd = opendev(&__file_dev, NULL, O_RDONLY);
+ if (fd < 0)
+ return -1;
- if (err)
- printf("ERROR: No configuration file found\n");
+ fp = &__file_info[fd];
- set_flags(regs, err ? EFLAGS_ZF : 0);
-}
+ handle = this_fs->fs_ops->open_config(&fp->i.fd);
+ if (handle < 0) {
+ close(fd);
+ errno = ENOENT;
+ return -1;
+ }
-void pm_mangle_name(com32sys_t *regs)
-{
- const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
- char *dst = MK_PTR(regs->es, regs->edi.w[0]);
+ fp->i.offset = 0;
+ fp->i.nbytes = 0;
- mangle_name(dst, src);
+ return fd;
}
-void mangle_name(char *dst, const char *src)
+__export void mangle_name(char *dst, const char *src)
{
this_fs->fs_ops->mangle_name(dst, src);
}
-void getfssec(com32sys_t *regs)
-{
- int sectors;
- bool have_more;
- uint32_t bytes_read;
- char *buf;
- struct file *file;
- uint16_t handle;
-
- sectors = regs->ecx.w[0];
-
- handle = regs->esi.w[0];
- file = handle_to_file(handle);
-
- buf = MK_PTR(regs->es, regs->ebx.w[0]);
- bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
-
- /*
- * If we reach EOF, the filesystem driver will have already closed
- * the underlying file... this really should be cleaner.
- */
- if (!have_more) {
- _close_file(file);
- regs->esi.w[0] = 0;
- }
-
- regs->ecx.l = bytes_read;
-}
-
-void getfsbytes(com32sys_t *regs)
-{
- int sectors;
- bool have_more;
- uint32_t bytes_read;
- char *buf;
- struct file *file;
- uint16_t handle;
-
- handle = regs->esi.w[0];
- file = handle_to_file(handle);
-
- sectors = regs->ecx.w[0] >> SECTOR_SHIFT(file->fs);
-
- buf = MK_PTR(regs->es, regs->ebx.w[0]);
- bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
-
- /*
- * If we reach EOF, the filesystem driver will have already closed
- * the underlying file... this really should be cleaner.
- */
- if (!have_more) {
- _close_file(file);
- regs->esi.w[0] = 0;
- }
-
- regs->ecx.l = bytes_read;
-}
-
size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
{
bool have_more;
@@ -183,24 +137,7 @@ size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
return bytes_read;
}
-void pm_searchdir(com32sys_t *regs)
-{
- char *name = MK_PTR(regs->ds, regs->edi.w[0]);
- int rv;
-
- rv = searchdir(name);
- if (rv < 0) {
- regs->esi.w[0] = 0;
- regs->eax.l = 0;
- regs->eflags.l |= EFLAGS_ZF;
- } else {
- regs->esi.w[0] = rv;
- regs->eax.l = handle_to_file(rv)->inode->size;
- regs->eflags.l &= ~EFLAGS_ZF;
- }
-}
-
-int searchdir(const char *name)
+int searchdir(const char *name, int flags)
{
static char root_name[] = "/";
struct file *file;
@@ -217,7 +154,7 @@ int searchdir(const char *name)
/* if we have ->searchdir method, call it */
if (file->fs->fs_ops->searchdir) {
- file->fs->fs_ops->searchdir(name, file);
+ file->fs->fs_ops->searchdir(name, flags, file);
if (file->inode)
return file_to_handle(file);
@@ -398,7 +335,7 @@ err_no_close:
return -1;
}
-int open_file(const char *name, struct com32_filedata *filedata)
+__export int open_file(const char *name, int flags, struct com32_filedata *filedata)
{
int rv;
struct file *file;
@@ -407,7 +344,7 @@ int open_file(const char *name, struct com32_filedata *filedata)
dprintf("open_file %s\n", name);
mangle_name(mangled_name, name);
- rv = searchdir(mangled_name);
+ rv = searchdir(mangled_name, flags);
if (rv < 0)
return rv;
@@ -426,29 +363,7 @@ int open_file(const char *name, struct com32_filedata *filedata)
return rv;
}
-void pm_open_file(com32sys_t *regs)
-{
- int rv;
- struct file *file;
- const char *name = MK_PTR(regs->es, regs->esi.w[0]);
- char mangled_name[FILENAME_MAX];
-
- dprintf("pm_open_file %s\n", name);
-
- mangle_name(mangled_name, name);
- rv = searchdir(mangled_name);
- if (rv < 0) {
- regs->eflags.l |= EFLAGS_CF;
- } else {
- file = handle_to_file(rv);
- regs->eflags.l &= ~EFLAGS_CF;
- regs->eax.l = file->inode->size;
- regs->ecx.w[0] = SECTOR_SIZE(file->fs);
- regs->esi.w[0] = rv;
- }
-}
-
-void close_file(uint16_t handle)
+__export void close_file(uint16_t handle)
{
struct file *file;
@@ -458,11 +373,6 @@ void close_file(uint16_t handle)
}
}
-void pm_close_file(com32sys_t *regs)
-{
- close_file(regs->esi.w[0]);
-}
-
/*
* it will do:
* initialize the memory management function;
@@ -488,9 +398,6 @@ void fs_init(com32sys_t *regs)
/* ops is a ptr list for several fs_ops */
const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
- /* Initialize malloc() */
- mem_init();
-
/* Default name for the root directory */
fs.cwd_name[0] = '/';
@@ -532,6 +439,11 @@ void fs_init(com32sys_t *regs)
dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd);
}
+ if (fs.fs_ops->chdir_start) {
+ if (fs.fs_ops->chdir_start() < 0)
+ printf("Failed to chdir to start directory\n");
+ }
+
SectorShift = fs.sector_shift;
SectorSize = fs.sector_size;
}
diff --git a/core/fs/getcwd.c b/core/fs/getcwd.c
index a7b6c7a9..70b93152 100644
--- a/core/fs/getcwd.c
+++ b/core/fs/getcwd.c
@@ -1,7 +1,7 @@
#include <string.h>
#include "fs.h"
-char *getcwd(char *buf, size_t size)
+__export char *core_getcwd(char *buf, size_t size)
{
char *ret = NULL;
diff --git a/core/fs/iso9660/iso9660.c b/core/fs/iso9660/iso9660.c
index 3cd3ac46..fe58a5b3 100644
--- a/core/fs/iso9660/iso9660.c
+++ b/core/fs/iso9660/iso9660.c
@@ -6,6 +6,7 @@
#include <cache.h>
#include <disk.h>
#include <fs.h>
+#include <stdlib.h>
#include "iso9660_fs.h"
/* Convert to lower case string */
@@ -226,7 +227,7 @@ static int iso_readdir(struct file *file, struct dirent *dirent)
}
/* Load the config file, return 1 if failed, or 0 */
-static int iso_load_config(void)
+static int iso_open_config(struct com32_filedata *filedata)
{
static const char *search_directories[] = {
"/boot/isolinux",
@@ -242,7 +243,7 @@ static int iso_load_config(void)
NULL
};
- return search_config(search_directories, filenames);
+ return search_dirs(filedata, search_directories, filenames, ConfigName);
}
static int iso_fs_init(struct fs_info *fs)
@@ -293,7 +294,7 @@ const struct fs_ops iso_fs_ops = {
.getfssec = generic_getfssec,
.close_file = generic_close_file,
.mangle_name = generic_mangle_name,
- .load_config = iso_load_config,
+ .open_config = iso_open_config,
.iget_root = iso_iget_root,
.iget = iso_iget,
.readdir = iso_readdir,
diff --git a/core/fs/lib/chdir.c b/core/fs/lib/chdir.c
new file mode 100644
index 00000000..715284bb
--- /dev/null
+++ b/core/fs/lib/chdir.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include <core.h>
+
+int generic_chdir_start(void)
+{
+ return chdir(CurrentDirName);
+}
diff --git a/core/fs/lib/loadconfig.c b/core/fs/lib/loadconfig.c
index c9652b6c..95e6f3f8 100644
--- a/core/fs/lib/loadconfig.c
+++ b/core/fs/lib/loadconfig.c
@@ -11,7 +11,7 @@
* directory, followed by a set of fallback directories. If found,
* set the current working directory to match.
*/
-int generic_load_config(void)
+int generic_open_config(struct com32_filedata *filedata)
{
static const char *search_directories[] = {
NULL, /* CurrentDirName */
@@ -30,5 +30,5 @@ int generic_load_config(void)
dprintf("CurrentDirName: \"%s\"\n", CurrentDirName);
- return search_config(search_directories, filenames);
+ return search_dirs(filedata, search_directories, filenames, ConfigName);
}
diff --git a/core/fs/lib/searchconfig.c b/core/fs/lib/searchconfig.c
index f18836a8..bb1dabf9 100644
--- a/core/fs/lib/searchconfig.c
+++ b/core/fs/lib/searchconfig.c
@@ -1,36 +1,37 @@
#include <dprintf.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <core.h>
#include <fs.h>
+__export char ConfigName[FILENAME_MAX];
+__export char config_cwd[FILENAME_MAX];
+
/*
- * Common implementation of load_config
- *
* This searches for a specified set of filenames in a specified set
* of directories. If found, set the current working directory to
* match.
*/
-int search_config(const char *search_directories[], const char *filenames[])
+int search_dirs(struct com32_filedata *filedata,
+ const char *search_directories[],
+ const char *filenames[],
+ char *realname)
{
- char confignamebuf[FILENAME_MAX];
- com32sys_t regs;
+ char namebuf[FILENAME_MAX];
const char *sd, **sdp;
const char *sf, **sfp;
for (sdp = search_directories; (sd = *sdp); sdp++) {
for (sfp = filenames; (sf = *sfp); sfp++) {
- memset(&regs, 0, sizeof regs);
- snprintf(confignamebuf, sizeof confignamebuf,
+ snprintf(namebuf, sizeof namebuf,
"%s%s%s",
sd, (*sd && sd[strlen(sd)-1] == '/') ? "" : "/",
sf);
- if (realpath(ConfigName, confignamebuf, FILENAME_MAX) == (size_t)-1)
+ if (realpath(realname, namebuf, FILENAME_MAX) == (size_t)-1)
continue;
- regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
- dprintf("Config search: %s\n", ConfigName);
- call16(core_open, &regs, &regs);
- if (!(regs.eflags.l & EFLAGS_ZF)) {
+ dprintf("Config search: %s\n", realname);
+ if (open_file(realname, O_RDONLY, filedata) >= 0) {
chdir(sd);
return 0; /* Got it */
}
diff --git a/core/fs/newconfig.c b/core/fs/newconfig.c
deleted file mode 100644
index 58c47a51..00000000
--- a/core/fs/newconfig.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2010 Intel Corporation; author: H. Peter Anvin
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston MA 02110-1301, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * newconfig.c
- *
- * Load a new configuration file
- */
-
-#include "core.h"
-#include "fs.h"
-
-void pm_is_config_file(com32sys_t *regs)
-{
- char target_cwd[FILENAME_MAX];
- const char *p;
-
- (void)regs;
-
- /* Save configuration file as an absolute path for posterity */
- realpath(ConfigName, KernelName, FILENAME_MAX);
-
- /* If we got anything on the command line, do a chdir */
- p = cmd_line;
- while (*p && !not_whitespace(*p))
- p++;
-
- if (*p) {
- mangle_name(target_cwd, p);
- chdir(target_cwd);
- }
-}
diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
index 500d0fd3..f54df7e5 100644
--- a/core/fs/ntfs/ntfs.c
+++ b/core/fs/ntfs/ntfs.c
@@ -1380,7 +1380,7 @@ const struct fs_ops ntfs_fs_ops = {
.getfssec = ntfs_getfssec,
.close_file = generic_close_file,
.mangle_name = generic_mangle_name,
- .load_config = generic_load_config,
+ .open_config = generic_open_config,
.readdir = ntfs_readdir,
.iget_root = ntfs_iget_root,
.iget = ntfs_iget,
diff --git a/core/fs/pxe/core.c b/core/fs/pxe/core.c
new file mode 100644
index 00000000..e7dc8fea
--- /dev/null
+++ b/core/fs/pxe/core.c
@@ -0,0 +1,262 @@
+#include <syslinux/pxe_api.h>
+#include <lwip/api.h>
+#include <lwip/tcpip.h>
+#include <lwip/dns.h>
+#include <core.h>
+#include <net.h>
+#include "pxe.h"
+
+#include <dprintf.h>
+
+const struct url_scheme url_schemes[] = {
+ { "tftp", tftp_open, 0 },
+ { "http", http_open, O_DIRECTORY },
+ { "ftp", ftp_open, O_DIRECTORY },
+ { NULL, NULL, 0 },
+};
+
+/**
+ * Open a socket
+ *
+ * @param:socket, the socket to open
+ * @param:proto, the protocol of the new connection
+ *
+ * @out: error code, 0 on success, -1 on failure
+ */
+int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto)
+{
+ struct net_private_lwip *priv = &socket->net.lwip;
+ enum netconn_type type;
+ int err;
+
+ switch (proto) {
+ case NET_CORE_TCP:
+ type = NETCONN_TCP;
+ break;
+ case NET_CORE_UDP:
+ type = NETCONN_UDP;
+ break;
+ default:
+ type = NETCONN_INVALID;
+ break;
+ }
+
+ priv->conn = netconn_new(type);
+ if (!priv->conn)
+ return -1;
+
+ priv->conn->recv_timeout = 15; /* A 15 ms recv timeout... */
+ err = netconn_bind(priv->conn, NULL, 0);
+ if (err) {
+ ddprintf("netconn_bind error %d\n", err);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Close a socket
+ *
+ * @param:socket, the socket to open
+ */
+void net_core_close(struct pxe_pvt_inode *socket)
+{
+ struct net_private_lwip *priv = &socket->net.lwip;
+
+ if (priv->conn) {
+ netconn_delete(priv->conn);
+ priv->conn = NULL;
+ }
+}
+
+/**
+ * Establish a connection on an open socket
+ *
+ * @param:socket, the open socket
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+ uint16_t port)
+{
+ struct net_private_lwip *priv = &socket->net.lwip;
+ struct ip_addr addr;
+
+ dprintf("net_core_connect: %08X %04X\n", ntohl(ip), port);
+ addr.addr = ip;
+ netconn_connect(priv->conn, &addr, port);
+}
+
+/**
+ * Tear down a connection on an open socket
+ *
+ * @param:socket, the open socket
+ */
+void net_core_disconnect(struct pxe_pvt_inode *socket)
+{
+ struct net_private_lwip *priv = &socket->net.lwip;
+ netconn_disconnect(priv->conn);
+}
+
+/**
+ * Read data from the network stack
+ *
+ * @param:socket, the open socket
+ * @param:buf, location of buffer to store data
+ * @param:buf_len, size of buffer
+
+ * @out: src_ip, ip address of the data source
+ * @out: src_port, port number of the data source, host-byte order
+ */
+int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+ uint32_t *src_ip, uint16_t *src_port)
+{
+ struct net_private_lwip *priv = &socket->net.lwip;
+ struct netbuf *nbuf;
+ u16_t nbuf_len;
+ int err;
+
+ err = netconn_recv(priv->conn, &nbuf);
+ if (err)
+ return err;
+
+ if (!nbuf)
+ return -1;
+
+ *src_ip = netbuf_fromaddr(nbuf)->addr;
+ *src_port = netbuf_fromport(nbuf);
+
+ netbuf_first(nbuf); /* XXX needed? */
+ nbuf_len = netbuf_len(nbuf);
+ if (nbuf_len <= *buf_len)
+ netbuf_copy(nbuf, buf, nbuf_len);
+ else
+ nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */
+ netbuf_delete(nbuf);
+
+ *buf_len = nbuf_len;
+ return 0;
+}
+
+/**
+ * Send a UDP packet.
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ */
+void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+{
+ struct netconn *conn = socket->net.lwip.conn;
+ struct netbuf *nbuf;
+ void *pbuf;
+ int err;
+
+ nbuf = netbuf_new();
+ if (!nbuf) {
+ ddprintf("netbuf allocation error\n");
+ return;
+ }
+
+ pbuf = netbuf_alloc(nbuf, len);
+ if (!pbuf) {
+ ddprintf("pbuf allocation error\n");
+ goto out;
+ }
+
+ memcpy(pbuf, data, len);
+
+ err = netconn_send(conn, nbuf);
+ if (err) {
+ ddprintf("netconn_send error %d\n", err);
+ goto out;
+ }
+
+out:
+ netbuf_delete(nbuf);
+}
+
+ /**
+ * Send a UDP packet to a destination
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_sendto(struct pxe_pvt_inode *socket, const void *data,
+ size_t len, uint32_t ip, uint16_t port)
+{
+ struct netconn *conn = socket->net.lwip.conn;
+ struct ip_addr addr;
+ struct netbuf *nbuf;
+ void *pbuf;
+ int err;
+
+ nbuf = netbuf_new();
+ if (!nbuf) {
+ ddprintf("netbuf allocation error\n");
+ return;
+ }
+
+ pbuf = netbuf_alloc(nbuf, len);
+ if (!pbuf) {
+ ddprintf("pbuf allocation error\n");
+ goto out;
+ }
+
+ memcpy(pbuf, data, len);
+
+ dprintf("net_core_sendto: %08X %04X\n", ntohl(ip), port);
+ addr.addr = ip;
+
+ err = netconn_sendto(conn, nbuf, &addr, port);
+ if (err) {
+ ddprintf("netconn_sendto error %d\n", err);
+ goto out;
+ }
+
+out:
+ netbuf_delete(nbuf);
+}
+
+/**
+ * Network stack-specific initialization
+ */
+void net_core_init(void)
+{
+ int err;
+ int i;
+
+ http_bake_cookies();
+
+ /* Initialize lwip */
+ tcpip_init(NULL, NULL);
+
+ /* Start up the undi driver for lwip */
+ err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway);
+ if (err) {
+ ddprintf("undiif driver failed to start: %d\n", err);
+ kaboom();
+ }
+
+ for (i = 0; i < DNS_MAX_SERVERS; i++) {
+ /* Transfer the DNS information to lwip */
+ dns_setserver(i, (struct ip_addr *)&dns_server[i]);
+ }
+}
+
+void probe_undi(void)
+{
+ /* Probe UNDI information */
+ pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info);
+ pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface);
+
+ ddprintf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
+ pxe_undi_info.BaseIo, pxe_undi_info.IntNumber,
+ pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType,
+ pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags);
+}
+
diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c
index 50f2de04..75827ff7 100644
--- a/core/fs/pxe/dhcp_option.c
+++ b/core/fs/pxe/dhcp_option.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <core.h>
#include <sys/cpu.h>
+#include <lwip/opt.h> /* DNS_MAX_SERVERS */
#include "pxe.h"
char LocalDomain[256];
@@ -48,13 +49,8 @@ static void dns_servers(const void *data, int opt_len)
static void local_domain(const void *data, int opt_len)
{
- char buffer[256];
- char *ld = LocalDomain;
-
- memcpy(buffer, data, opt_len);
- buffer[opt_len] = 0;
-
- dns_mangle(&ld, buffer);
+ memcpy(LocalDomain, data, opt_len);
+ LocalDomain[opt_len] = 0;
}
static void vendor_encaps(const void *data, int opt_len)
@@ -227,12 +223,10 @@ static void parse_dhcp_options(const void *option, int size, uint8_t opt_filter)
* LocalDomain - Local domain name
* MAC_len, MAC - Client identifier, if MAC_len == 0
*
- * This assumes the DHCP packet is in "trackbuf".
- *
*/
-void parse_dhcp(int pkt_len)
+void parse_dhcp(const void *pkt, size_t pkt_len)
{
- struct bootp_t *dhcp = (struct bootp_t *)trackbuf;
+ const struct bootp_t *dhcp = (const struct bootp_t *)pkt;
int opt_len;
IPInfo.ipv4 = 4; /* This is IPv4 only for now... */
diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c
index 641ea389..afb9e219 100644
--- a/core/fs/pxe/dnsresolv.c
+++ b/core/fs/pxe/dnsresolv.c
@@ -2,6 +2,8 @@
#include <string.h>
#include <core.h>
#include "pxe.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
/* DNS CLASS values we care about */
#define CLASS_IN 1
@@ -48,300 +50,83 @@ struct dnsrr {
uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
-
/*
- * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
- * number of dots encountered. On return, *dst is updated.
+ * parse the ip_str and return the ip address with *res.
+ * return true if the whole string was consumed and the result
+ * was valid.
+ *
*/
-int dns_mangle(char **dst, const char *p)
+static bool parse_dotquad(const char *ip_str, uint32_t *res)
{
- char *q = *dst;
- char *count_ptr;
- char c;
- int dots = 0;
-
- count_ptr = q;
- *q++ = 0;
-
- while (1) {
- c = *p++;
- if (c == 0 || c == ':' || c == '/')
- break;
- if (c == '.') {
- dots++;
- count_ptr = q;
- *q++ = 0;
- continue;
+ const char *p = ip_str;
+ uint8_t part = 0;
+ uint32_t ip = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ while (is_digit(*p)) {
+ part = part * 10 + *p - '0';
+ p++;
}
+ if (i != 3 && *p != '.')
+ return false;
- *count_ptr += 1;
- *q++ = c;
+ ip = (ip << 8) | part;
+ part = 0;
+ p++;
}
+ p--;
- if (*count_ptr)
- *q++ = 0;
-
- /* update the strings */
- *dst = q;
- return dots;
+ *res = htonl(ip);
+ return *p == '\0';
}
-
/*
- * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
- * is allowed pointers relative to a packet in buf.
+ * Actual resolver function.
*
+ * Points to a null-terminated in _name_ and returns the ip addr in
+ * _ip_ if it exists and can be found. If _ip_ = 0 on exit, the
+ * lookup failed. _name_ will be updated
*/
-static bool dns_compare(const void *s1, const void *s2, const void *buf)
-{
- const uint8_t *q = s1;
- const uint8_t *p = s2;
- unsigned int c0, c1;
-
- while (1) {
- c0 = p[0];
- if (c0 >= 0xc0) {
- /* Follow pointer */
- c1 = p[1];
- p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
- } else if (c0) {
- c0++; /* Include the length byte */
- if (memcmp(q, p, c0))
- return false;
- q += c0;
- p += c0;
- } else {
- return *q == 0;
- }
- }
-}
-
-/*
- * Copy a DNS label into a buffer, considering the possibility that we might
- * have to follow pointers relative to "buf".
- * Returns a pointer to the first free byte *after* the terminal null.
- */
-static void *dns_copylabel(void *dst, const void *src, const void *buf)
+__export uint32_t dns_resolv(const char *name)
{
- uint8_t *q = dst;
- const uint8_t *p = src;
- unsigned int c0, c1;
-
- while (1) {
- c0 = p[0];
- if (c0 >= 0xc0) {
- /* Follow pointer */
- c1 = p[1];
- p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
- } else if (c0) {
- c0++; /* Include the length byte */
- memcpy(q, p, c0);
- p += c0;
- q += c0;
- } else {
- *q++ = 0;
- return q;
- }
- }
-}
-
-/*
- * Skip past a DNS label set in DS:SI
- */
-static char *dns_skiplabel(char *label)
-{
- uint8_t c;
+ err_t err;
+ struct ip_addr ip;
+ char fullname[512];
+
+ /*
+ * Return failure on an empty input... this can happen during
+ * some types of URL parsing, and this is the easiest place to
+ * check for it.
+ */
+ if (!name || !*name)
+ return 0;
- while (1) {
- c = *label++;
- if (c >= 0xc0)
- return ++label; /* pointer is two bytes */
- if (c == 0)
- return label;
- label += c;
- }
-}
-
-/*
- * Actual resolver function
- * Points to a null-terminated or :-terminated string in _name_
- * and returns the ip addr in _ip_ if it exists and can be found.
- * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
- *
- * XXX: probably need some caching here.
- */
-uint32_t dns_resolv(const char *name)
-{
- static char __lowmem DNSSendBuf[PKTBUF_SIZE];
- static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
- char *p;
- int err;
- int dots;
- int same;
- int rd_len;
- int ques, reps; /* number of questions and replies */
- uint8_t timeout;
- const uint8_t *timeout_ptr = TimeoutTable;
- uint32_t oldtime;
- uint32_t srv;
- uint32_t *srv_ptr;
- struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
- struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
- struct dnsquery *query;
- struct dnsrr *rr;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
- uint16_t local_port;
- uint32_t result = 0;
+ /* If it is a valid dot quad, just return that value */
+ if (parse_dotquad(name, &ip.addr))
+ return ip.addr;
/* Make sure we have at least one valid DNS server */
- if (!dns_server[0])
+ if (!dns_getserver(0).addr)
return 0;
- /* Get a local port number */
- local_port = get_port();
-
- /* First, fill the DNS header struct */
- hd1->id++; /* New query ID */
- hd1->flags = htons(0x0100); /* Recursion requested */
- hd1->qdcount = htons(1); /* One question */
- hd1->ancount = 0; /* No answers */
- hd1->nscount = 0; /* No NS */
- hd1->arcount = 0; /* No AR */
-
- p = DNSSendBuf + sizeof(struct dnshdr);
- dots = dns_mangle(&p, name); /* store the CNAME */
-
- if (!dots) {
- p--; /* Remove final null */
- /* Uncompressed DNS label set so it ends in null */
- p = stpcpy(p, LocalDomain);
- }
-
- /* Fill the DNS query packet */
- query = (struct dnsquery *)p;
- query->qtype = htons(TYPE_A);
- query->qclass = htons(CLASS_IN);
- p += sizeof(struct dnsquery);
-
- /* Now send it to name server */
- timeout_ptr = TimeoutTable;
- timeout = *timeout_ptr++;
- srv_ptr = dns_server;
- while (timeout) {
- srv = *srv_ptr++;
- if (!srv) {
- srv_ptr = dns_server;
- srv = *srv_ptr++;
- }
-
- udp_write.status = 0;
- udp_write.ip = srv;
- udp_write.gw = gateway(srv);
- udp_write.src_port = local_port;
- udp_write.dst_port = DNS_PORT;
- udp_write.buffer_size = p - DNSSendBuf;
- udp_write.buffer = FAR_PTR(DNSSendBuf);
- err = pxe_call(PXENV_UDP_WRITE, &udp_write);
- if (err || udp_write.status)
- continue;
-
- oldtime = jiffies();
- do {
- if (jiffies() - oldtime >= timeout)
- goto again;
-
- udp_read.status = 0;
- udp_read.src_ip = srv;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.s_port = DNS_PORT;
- udp_read.d_port = local_port;
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.buffer = FAR_PTR(DNSRecvBuf);
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- } while (err || udp_read.status || hd2->id != hd1->id);
-
- if ((hd2->flags ^ 0x80) & htons(0xf80f))
- goto badness;
-
- ques = htons(hd2->qdcount); /* Questions */
- reps = htons(hd2->ancount); /* Replies */
- p = DNSRecvBuf + sizeof(struct dnshdr);
- while (ques--) {
- p = dns_skiplabel(p); /* Skip name */
- p += 4; /* Skip question trailer */
- }
-
- /* Parse the replies */
- while (reps--) {
- same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
- p, DNSRecvBuf);
- p = dns_skiplabel(p);
- rr = (struct dnsrr *)p;
- rd_len = ntohs(rr->rdlength);
- if (same && ntohs(rr->class) == CLASS_IN) {
- switch (ntohs(rr->type)) {
- case TYPE_A:
- if (rd_len == 4) {
- result = *(uint32_t *)rr->rdata;
- goto done;
- }
- break;
- case TYPE_CNAME:
- dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
- rr->rdata, DNSRecvBuf);
- /*
- * We should probably rescan the packet from the top
- * here, and technically we might have to send a whole
- * new request here...
- */
- break;
- default:
- break;
- }
- }
-
- /* not the one we want, try next */
- p += sizeof(struct dnsrr) + rd_len;
- }
-
- badness:
- /*
- *
- ; We got back no data from this server.
- ; Unfortunately, for a recursive, non-authoritative
- ; query there is no such thing as an NXDOMAIN reply,
- ; which technically means we can't draw any
- ; conclusions. However, in practice that means the
- ; domain doesn't exist. If this turns out to be a
- ; problem, we may want to add code to go through all
- ; the servers before giving up.
-
- ; If the DNS server wasn't capable of recursion, and
- ; isn't capable of giving us an authoritative reply
- ; (i.e. neither AA or RA set), then at least try a
- ; different setver...
- */
- if (hd2->flags == htons(0x480))
- continue;
-
- break; /* failed */
-
- again:
- continue;
+ /* Is it a local (unqualified) domain name? */
+ if (!strchr(name, '.') && LocalDomain[0]) {
+ snprintf(fullname, sizeof fullname, "%s.%s", name, LocalDomain);
+ name = fullname;
}
-done:
- free_port(local_port); /* Return port number to the free pool */
+ err = netconn_gethostbyname(name, &ip);
+ if (err)
+ return 0;
- return result;
+ return ip.addr;
}
-
/*
* the one should be called from ASM file
*/
-void pxe_dns_resolv(com32sys_t *regs)
+void pm_pxe_dns_resolv(com32sys_t *regs)
{
const char *name = MK_PTR(regs->ds, regs->esi.w[0]);
diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c
new file mode 100644
index 00000000..c2d155ae
--- /dev/null
+++ b/core/fs/pxe/ftp.c
@@ -0,0 +1,280 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ftp.c
+ */
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include <netinet/in.h>
+#include <lwip/api.h>
+#include "core.h"
+#include "fs.h"
+#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+
+static int ftp_cmd_response(struct inode *inode, const char *cmd,
+ const char *cmd_arg,
+ uint8_t *pasv_data, int *pn_ptr)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int c;
+ int pos, code;
+ int pb, pn;
+ bool ps;
+ bool first_line, done;
+ err_t err;
+ char cmd_buf[4096];
+ int cmd_len;
+ const char *p;
+ char *q;
+
+ if (cmd) {
+ cmd_len = strlcpy(cmd_buf, cmd, sizeof cmd_buf);
+ if (cmd_len >= sizeof cmd_buf - 3)
+ return -1;
+ q = cmd_buf + cmd_len;
+
+ if (cmd_arg) {
+ p = cmd_arg;
+
+ *q++ = ' ';
+ cmd_len++;
+ while (*p) {
+ if (++cmd_len < sizeof cmd_buf) *q++ = *p;
+ if (*p == '\r')
+ if (++cmd_len < sizeof cmd_buf) *q++ = '\0';
+ p++;
+ }
+
+ if (cmd_len >= sizeof cmd_buf - 2)
+ return -1;
+ }
+
+ *q++ = '\r';
+ *q++ = '\n';
+ cmd_len += 2;
+
+ err = netconn_write(socket->net.lwip.conn, cmd_buf, cmd_len, NETCONN_COPY);
+ if (err)
+ return -1;
+ }
+
+ pos = code = pn = pb = 0;
+ ps = false;
+ first_line = true;
+ done = false;
+
+ while ((c = pxe_getc(inode)) >= 0) {
+ if (c == '\n') {
+ if (done) {
+ if (pn) {
+ pn += ps;
+ if (pn_ptr)
+ *pn_ptr = pn;
+ }
+ return code;
+ }
+ pos = code = 0;
+ first_line = false;
+ continue;
+ }
+
+ switch (pos++) {
+ case 0:
+ case 1:
+ case 2:
+ if (c < '0' || c > '9') {
+ if (first_line)
+ return -1;
+ else
+ pos = 4; /* Skip this line */
+ } else {
+ code = (code*10) + (c - '0');
+ }
+ break;
+
+ case 3:
+ pn = pb = 0;
+ ps = false;
+ if (c == ' ')
+ done = true;
+ else if (c == '-')
+ done = false;
+ else if (first_line)
+ return -1;
+ else
+ done = false;
+ break;
+
+ default:
+ if (pasv_data) {
+ if (c >= '0' && c <= '9') {
+ pb = (pb*10) + (c-'0');
+ if (pn < 6)
+ pasv_data[pn] = pb;
+ ps = true;
+ } else if (c == ',') {
+ pn++;
+ pb = 0;
+ ps = false;
+ } else if (pn) {
+ pn += ps;
+ if (pn_ptr)
+ *pn_ptr = pn;
+ pn = pb = 0;
+ ps = false;
+ }
+ }
+ break;
+ }
+ }
+
+ return -1;
+}
+
+static void ftp_free(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->ctl) {
+ tcp_close_file(socket->ctl);
+ free_socket(socket->ctl);
+ socket->ctl = NULL;
+ }
+ tcp_close_file(inode);
+}
+
+static void ftp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ struct pxe_pvt_inode *ctlsock;
+ int resp;
+
+ ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
+ if (ctlsock->net.lwip.conn) {
+ resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL);
+ while (resp == 226) {
+ resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+ }
+ }
+ ftp_free(inode);
+}
+
+static const struct pxe_conn_ops ftp_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = ftp_close_file,
+ .readdir = ftp_readdir,
+};
+
+void ftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ struct pxe_pvt_inode *ctlsock;
+ struct ip_addr addr;
+ uint8_t pasv_data[6];
+ int pasv_bytes;
+ int resp;
+ err_t err;
+
+ (void)redir; /* FTP does not redirect */
+
+ inode->size = 0;
+
+ if (!url->port)
+ url->port = 21;
+
+ url_unescape(url->path, 0);
+
+ socket->ops = &ftp_conn_ops;
+
+ /* Set up the control connection */
+ socket->ctl = alloc_inode(inode->fs, 0, sizeof(struct pxe_pvt_inode));
+ if (!socket->ctl)
+ return;
+ ctlsock = PVT(socket->ctl);
+ ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */
+ ctlsock->net.lwip.conn = netconn_new(NETCONN_TCP);
+ if (!ctlsock->net.lwip.conn)
+ goto err_free;
+ addr.addr = url->ip;
+ err = netconn_connect(ctlsock->net.lwip.conn, &addr, url->port);
+ if (err)
+ goto err_delete;
+
+ do {
+ resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+ } while (resp == 120);
+ if (resp != 220)
+ goto err_disconnect;
+
+ if (!url->user)
+ url->user = "anonymous";
+ if (!url->passwd)
+ url->passwd = "syslinux@";
+
+ resp = ftp_cmd_response(socket->ctl, "USER", url->user, NULL, NULL);
+ if (resp != 202 && resp != 230) {
+ if (resp != 331)
+ goto err_disconnect;
+
+ resp = ftp_cmd_response(socket->ctl, "PASS", url->passwd, NULL, NULL);
+ if (resp != 230)
+ goto err_disconnect;
+ }
+
+ if (!(flags & O_DIRECTORY)) {
+ resp = ftp_cmd_response(socket->ctl, "TYPE", "I", NULL, NULL);
+ if (resp != 200)
+ goto err_disconnect;
+ }
+
+ resp = ftp_cmd_response(socket->ctl, "PASV", NULL, pasv_data, &pasv_bytes);
+ if (resp != 227 || pasv_bytes != 6)
+ goto err_disconnect;
+
+ socket->net.lwip.conn = netconn_new(NETCONN_TCP);
+ if (!socket->net.lwip.conn)
+ goto err_disconnect;
+ err = netconn_connect(socket->net.lwip.conn, (struct ip_addr *)&pasv_data[0],
+ ntohs(*(uint16_t *)&pasv_data[4]));
+ if (err)
+ goto err_disconnect;
+
+ resp = ftp_cmd_response(socket->ctl,
+ (flags & O_DIRECTORY) ? "LIST" : "RETR",
+ url->path, NULL, NULL);
+ if (resp != 125 && resp != 150)
+ goto err_disconnect;
+
+ inode->size = -1;
+ return; /* Sucess! */
+
+err_disconnect:
+ if (ctlsock->net.lwip.conn)
+ netconn_write(ctlsock->net.lwip.conn, "QUIT\r\n", 6, NETCONN_NOCOPY);
+ if (socket->net.lwip.conn)
+ netconn_delete(socket->net.lwip.conn);
+ if (ctlsock->net.lwip.buf)
+ netbuf_delete(ctlsock->net.lwip.buf);
+err_delete:
+ if (ctlsock->net.lwip.conn)
+ netconn_delete(ctlsock->net.lwip.conn);
+err_free:
+ free_socket(socket->ctl);
+}
diff --git a/core/fs/pxe/ftp_readdir.c b/core/fs/pxe/ftp_readdir.c
new file mode 100644
index 00000000..6b87f77e
--- /dev/null
+++ b/core/fs/pxe/ftp_readdir.c
@@ -0,0 +1,141 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ftp_readdir.c
+ */
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dprintf.h>
+#include "pxe.h"
+
+static int dirtype(char type)
+{
+ switch (type) {
+ case 'f':
+ return DT_FIFO;
+ case 'c':
+ return DT_CHR;
+ case 'd':
+ return DT_DIR;
+ case 'b':
+ return DT_BLK;
+ case '-':
+ case '0' ... '9': /* Some DOS FTP stacks */
+ return DT_REG;
+ case 'l':
+ return DT_LNK;
+ case 's':
+ return DT_SOCK;
+ default:
+ return DT_UNKNOWN;
+ }
+}
+
+int ftp_readdir(struct inode *inode, struct dirent *dirent)
+{
+ char bufs[2][FILENAME_MAX + 1];
+ int nbuf = 0;
+ char *buf = bufs[nbuf];
+ char *p = buf;
+ char *name = NULL;
+ char type;
+ int c;
+ int dt;
+ bool was_cr = false;
+ bool first = true;
+
+ for (;;) {
+ type = 0;
+
+ for (;;) {
+ c = pxe_getc(inode);
+ if (c == -1)
+ return -1; /* Nothing else there */
+
+ if (c == '\r') {
+ was_cr = true;
+ continue;
+ }
+ if (was_cr) {
+ if (c == '\n') {
+ if (!name) {
+ *p = '\0';
+ name = buf;
+ }
+ break; /* End of line */
+ }
+ else if (c == '\0')
+ c = '\r';
+ }
+ was_cr = false;
+
+ if (c == ' ' || c == '\t') {
+ if (!name) {
+ *p = '\0';
+ if (first) {
+ if (p == buf) {
+ /* Name started with whitespace - skip line */
+ name = buf;
+ } else if ((p = strchr(buf, ';'))) {
+ /* VMS/Multinet format */
+ if (p > buf+4 && !memcmp(p-4, ".DIR", 4)) {
+ type = 'd';
+ p -= 4;
+ } else {
+ type = 'f';
+ }
+ *p = '\0';
+ name = buf;
+ } else {
+ type = buf[0];
+ }
+ first = false;
+ } else {
+ /* Not the first word */
+ if ((type >= '0' && type <= '9') &&
+ !strcmp(buf, "<DIR>")) {
+ /* Some DOS FTP servers */
+ type = 'd';
+ } else if (type == 'l' && !strcmp(buf, "->")) {
+ /* The name was the previous word */
+ name = bufs[nbuf ^ 1];
+ }
+ }
+ nbuf ^= 1;
+ p = buf = bufs[nbuf];
+ }
+ } else {
+ if (!name && p < buf + FILENAME_MAX)
+ *p++ = c;
+ }
+ }
+
+ dt = dirtype(type);
+ if (dt != DT_UNKNOWN) {
+ size_t len = strlen(name);
+
+ if (len <= NAME_MAX) {
+ dirent->d_type = dt;
+ dirent->d_ino = 0; /* Not applicable */
+ dirent->d_off = 0; /* Not applicable */
+ dirent->d_reclen = offsetof(struct dirent, d_name) + len+1;
+ memcpy(dirent->d_name, name, len+1);
+ return 0;
+ }
+ }
+
+ /* Otherwise try the next line... */
+ }
+}
diff --git a/core/fs/pxe/gpxeurl.c b/core/fs/pxe/gpxeurl.c
new file mode 100644
index 00000000..6bbae3c2
--- /dev/null
+++ b/core/fs/pxe/gpxeurl.c
@@ -0,0 +1,88 @@
+#include "pxe.h"
+#if GPXE
+
+static void gpxe_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_CLOSE file_close;
+
+ file_close.FileHandle = socket->tftp_remoteport;
+ pxe_call(PXENV_FILE_CLOSE, &file_close);
+}
+
+/**
+ * Get a fresh packet from a gPXE socket
+ * @param: inode -> Inode pointer
+ *
+ */
+static void gpxe_get_packet(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_READ file_read;
+ int err;
+
+ while (1) {
+ file_read.FileHandle = socket->tftp_remoteport;
+ file_read.Buffer = FAR_PTR(packet_buf);
+ file_read.BufferSize = PKTBUF_SIZE;
+ err = pxe_call(PXENV_FILE_READ, &file_read);
+ if (!err) /* successed */
+ break;
+
+ if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
+ kaboom();
+ }
+
+ memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
+
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ socket->tftp_bytesleft = file_read.BufferSize;
+ socket->tftp_filepos += file_read.BufferSize;
+
+ if (socket->tftp_bytesleft == 0)
+ inode->size = socket->tftp_filepos;
+
+ /* if we're done here, close the file */
+ if (inode->size > socket->tftp_filepos)
+ return;
+
+ /* Got EOF, close it */
+ socket->tftp_goteof = 1;
+ gpxe_close_file(inode);
+}
+
+/**
+ * Open a url using gpxe
+ *
+ * @param:inode, the inode to store our state in
+ * @param:url, the url we want to open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+void gpxe_open(struct inode *inode, const char *url)
+{
+ static __lowmem struct s_PXENV_FILE_OPEN file_open;
+ static char lowurl[2*FILENAME_MAX];
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int err;
+
+ socket->tftp_pktbuf = malloc(PKTBUF_SIZE);
+ if (!socket->tftp_pktbuf)
+ return;
+
+ snprintf(lowurl, sizeof lowurl, "%s", url);
+ file_open.Status = PXENV_STATUS_BAD_FUNC;
+ file_open.FileName = FAR_PTR(lowurl);
+ err = pxe_call(PXENV_FILE_OPEN, &file_open);
+ if (err)
+ return;
+
+ socket->fill_buffer = gpxe_get_packet;
+ socket->close = gpxe_close_file;
+ socket->tftp_remoteport = file_open.FileHandle;
+ inode->size = -1; /* This is not an error */
+}
+
+#endif /* GPXE */
diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
new file mode 100644
index 00000000..94f1059a
--- /dev/null
+++ b/core/fs/pxe/http.c
@@ -0,0 +1,400 @@
+#include <syslinux/sysappend.h>
+#include <ctype.h>
+#include <lwip/api.h>
+#include "pxe.h"
+#include "../../../version.h"
+#include "url.h"
+
+#define HTTP_PORT 80
+
+static bool is_tspecial(int ch)
+{
+ bool tspecial = false;
+ switch(ch) {
+ case '(': case ')': case '<': case '>': case '@':
+ case ',': case ';': case ':': case '\\': case '"':
+ case '/': case '[': case ']': case '?': case '=':
+ case '{': case '}': case ' ': case '\t':
+ tspecial = true;
+ break;
+ }
+ return tspecial;
+}
+
+static bool is_ctl(int ch)
+{
+ return ch < 0x20;
+}
+
+static bool is_token(int ch)
+{
+ /* Can by antying except a ctl character or a tspecial */
+ return !is_ctl(ch) && !is_tspecial(ch);
+}
+
+static bool append_ch(char *str, size_t size, size_t *pos, int ch)
+{
+ bool success = true;
+ if ((*pos + 1) >= size) {
+ *pos = 0;
+ success = false;
+ } else {
+ str[*pos] = ch;
+ str[*pos + 1] = '\0';
+ *pos += 1;
+ }
+ return success;
+}
+
+static size_t cookie_len, header_len;
+static char *cookie_buf, *header_buf;
+
+__export uint32_t SendCookies = -1UL; /* Send all cookies */
+
+static size_t http_do_bake_cookies(char *q)
+{
+ static const char uchexchar[16] = "0123456789ABCDEF";
+ int i;
+ size_t n = 0;
+ const char *p;
+ char c;
+ bool first = true;
+ uint32_t mask = SendCookies;
+
+ for (i = 0; i < SYSAPPEND_MAX; i++) {
+ if ((mask & 1) && (p = sysappend_strings[i])) {
+ if (first) {
+ if (q) {
+ strcpy(q, "Cookie: ");
+ q += 8;
+ }
+ n += 8;
+ first = false;
+ }
+ if (q) {
+ strcpy(q, "_Syslinux_");
+ q += 10;
+ }
+ n += 10;
+ /* Copy string up to and including '=' */
+ do {
+ c = *p++;
+ if (q)
+ *q++ = c;
+ n++;
+ } while (c != '=');
+ while ((c = *p++)) {
+ if (c == ' ') {
+ if (q)
+ *q++ = '+';
+ n++;
+ } else if (is_token(c)) {
+ if (q)
+ *q++ = c;
+ n++;
+ } else {
+ if (q) {
+ *q++ = '%';
+ *q++ = uchexchar[c >> 4];
+ *q++ = uchexchar[c & 15];
+ }
+ n += 3;
+ }
+ }
+ if (q)
+ *q++ = ';';
+ n++;
+ }
+ mask >>= 1;
+ }
+ if (!first) {
+ if (q) {
+ *q++ = '\r';
+ *q++ = '\n';
+ }
+ n += 2;
+ }
+ if (q)
+ *q = '\0';
+
+ return n;
+}
+
+void http_bake_cookies(void)
+{
+ if (cookie_buf)
+ free(cookie_buf);
+
+ cookie_len = http_do_bake_cookies(NULL);
+ cookie_buf = malloc(cookie_len+1);
+ if (!cookie_buf) {
+ cookie_len = 0;
+ return;
+ }
+
+ if (header_buf)
+ free(header_buf);
+
+ header_len = cookie_len + 6*FILENAME_MAX + 256;
+ header_buf = malloc(header_len);
+ if (!header_buf) {
+ header_len = 0;
+ return; /* Uh-oh... */
+ }
+
+ http_do_bake_cookies(cookie_buf);
+}
+
+static const struct pxe_conn_ops http_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = tcp_close_file,
+ .readdir = http_readdir,
+};
+
+void http_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int header_bytes;
+ const char *next;
+ char field_name[20];
+ char field_value[1024];
+ size_t field_name_len, field_value_len;
+ err_t err;
+ enum state {
+ st_httpver,
+ st_stcode,
+ st_skipline,
+ st_fieldfirst,
+ st_fieldname,
+ st_fieldvalue,
+ st_skip_fieldname,
+ st_skip_fieldvalue,
+ st_eoh,
+ } state;
+ struct ip_addr addr;
+ static char location[FILENAME_MAX];
+ uint32_t content_length; /* same as inode->size */
+ size_t response_size;
+ int status;
+ int pos;
+
+ (void)flags;
+
+ if (!header_buf)
+ return; /* http is broken... */
+
+ /* This is a straightforward TCP connection after headers */
+ socket->ops = &http_conn_ops;
+
+ /* Reset all of the variables */
+ inode->size = content_length = -1;
+
+ /* Start the http connection */
+ socket->net.lwip.conn = netconn_new(NETCONN_TCP);
+ if (!socket->net.lwip.conn) {
+ printf("netconn_new failed\n");
+ return;
+ }
+
+ addr.addr = url->ip;
+ if (!url->port)
+ url->port = HTTP_PORT;
+
+ err = netconn_connect(socket->net.lwip.conn, &addr, url->port);
+ if (err) {
+ printf("netconn_connect error %d\n", err);
+ goto fail;
+ }
+
+ strcpy(header_buf, "GET /");
+ header_bytes = 5;
+ header_bytes += url_escape_unsafe(header_buf+5, url->path,
+ header_len - 5);
+ if (header_bytes >= header_len)
+ goto fail; /* Buffer overflow */
+ header_bytes += snprintf(header_buf + header_bytes,
+ header_len - header_bytes,
+ " HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "User-Agent: Syslinux/" VERSION_STR "\r\n"
+ "Connection: close\r\n"
+ "%s"
+ "\r\n",
+ url->host, cookie_buf ? cookie_buf : "");
+ if (header_bytes >= header_len)
+ goto fail; /* Buffer overflow */
+
+ err = netconn_write(socket->net.lwip.conn, header_buf,
+ header_bytes, NETCONN_NOCOPY);
+ if (err) {
+ printf("netconn_write error %d\n", err);
+ goto fail;
+ }
+
+ /* Parse the HTTP header */
+ state = st_httpver;
+ pos = 0;
+ status = 0;
+ response_size = 0;
+ field_value_len = 0;
+ field_name_len = 0;
+
+ while (state != st_eoh) {
+ int ch = pxe_getc(inode);
+ /* Eof before I finish paring the header */
+ if (ch == -1)
+ goto fail;
+#if 0
+ printf("%c", ch);
+#endif
+ response_size++;
+ if (ch == '\r' || ch == '\0')
+ continue;
+ switch (state) {
+ case st_httpver:
+ if (ch == ' ') {
+ state = st_stcode;
+ pos = 0;
+ }
+ break;
+
+ case st_stcode:
+ if (ch < '0' || ch > '9')
+ goto fail;
+ status = (status*10) + (ch - '0');
+ if (++pos == 3)
+ state = st_skipline;
+ break;
+
+ case st_skipline:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ break;
+
+ case st_fieldfirst:
+ if (ch == '\n')
+ state = st_eoh;
+ else if (isspace(ch)) {
+ /* A continuation line */
+ state = st_fieldvalue;
+ goto fieldvalue;
+ }
+ else if (is_token(ch)) {
+ /* Process the previous field before starting on the next one */
+ if (strcasecmp(field_name, "Content-Length") == 0) {
+ next = field_value;
+ /* Skip leading whitespace */
+ while (isspace(*next))
+ next++;
+ content_length = 0;
+ for (;(*next >= '0' && *next <= '9'); next++) {
+ if ((content_length * 10) < content_length)
+ break;
+ content_length = (content_length * 10) + (*next - '0');
+ }
+ /* In the case of overflow or other error ignore
+ * Content-Length.
+ */
+ if (*next)
+ content_length = -1;
+ }
+ else if (strcasecmp(field_name, "Location") == 0) {
+ next = field_value;
+ /* Skip leading whitespace */
+ while (isspace(*next))
+ next++;
+ strlcpy(location, next, sizeof location);
+ }
+ /* Start the field name and field value afress */
+ field_name_len = 1;
+ field_name[0] = ch;
+ field_name[1] = '\0';
+ field_value_len = 0;
+ field_value[0] = '\0';
+ state = st_fieldname;
+ }
+ else /* Bogus try to recover */
+ state = st_skipline;
+ break;
+
+ case st_fieldname:
+ if (ch == ':' ) {
+ state = st_fieldvalue;
+ }
+ else if (is_token(ch)) {
+ if (!append_ch(field_name, sizeof field_name, &field_name_len, ch))
+ state = st_skip_fieldname;
+ }
+ /* Bogus cases try to recover */
+ else if (ch == '\n')
+ state = st_fieldfirst;
+ else
+ state = st_skipline;
+ break;
+
+ case st_fieldvalue:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ else {
+ fieldvalue:
+ if (!append_ch(field_value, sizeof field_value, &field_value_len, ch))
+ state = st_skip_fieldvalue;
+ }
+ break;
+
+ /* For valid fields whose names are longer than I choose to support. */
+ case st_skip_fieldname:
+ if (ch == ':')
+ state = st_skip_fieldvalue;
+ else if (is_token(ch))
+ state = st_skip_fieldname;
+ /* Bogus cases try to recover */
+ else if (ch == '\n')
+ state = st_fieldfirst;
+ else
+ state = st_skipline;
+ break;
+
+ /* For valid fields whose bodies are longer than I choose to support. */
+ case st_skip_fieldvalue:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ break;
+
+ case st_eoh:
+ break; /* Should never happen */
+ }
+ }
+
+ if (state != st_eoh)
+ status = 0;
+
+ switch (status) {
+ case 200:
+ /*
+ * All OK, need to mark header data consumed and set up a file
+ * structure...
+ */
+ /* Treat the remainder of the bytes as data */
+ socket->tftp_filepos -= response_size;
+ break;
+ case 301:
+ case 302:
+ case 303:
+ case 307:
+ /* A redirect */
+ if (!location[0])
+ goto fail;
+ *redir = location;
+ goto fail;
+ default:
+ goto fail;
+ break;
+ }
+ return;
+fail:
+ inode->size = 0;
+ tcp_close_file(inode);
+ return;
+}
diff --git a/core/fs/pxe/http_readdir.c b/core/fs/pxe/http_readdir.c
new file mode 100644
index 00000000..b6e480e7
--- /dev/null
+++ b/core/fs/pxe/http_readdir.c
@@ -0,0 +1,471 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dprintf.h>
+#include "pxe.h"
+
+enum http_readdir_state {
+ st_start, /* 0 Initial state */
+ st_open, /* 1 "<" */
+ st_a, /* 2 "<a" */
+ st_attribute, /* 3 "<a " */
+ st_h, /* 4 "<a h" */
+ st_hr, /* 5 */
+ st_hre, /* 6 */
+ st_href, /* 7 */
+ st_hrefeq, /* 8 */
+ st_hrefqu, /* 9 */
+ st_badtag, /* 10 */
+ st_badtagqu, /* 11 */
+ st_badattr, /* 12 */
+ st_badattrqu, /* 13 */
+};
+
+struct machine {
+ char xchar;
+ uint8_t st_xchar;
+ uint8_t st_left; /* < */
+ uint8_t st_right; /* > */
+ uint8_t st_space; /* white */
+ uint8_t st_other; /* anything else */
+};
+
+static const struct machine statemachine[] = {
+ /* xchar st_xchar st_left st_right st_space st_other */
+ { 0, 0, st_open, st_start, st_start, st_start },
+ { 'a', st_a, st_badtag, st_start, st_open, st_badtag },
+ { 0, 0, st_open, st_open, st_attribute, st_badtag },
+ { 'h', st_h, st_open, st_start, st_attribute, st_badattr },
+ { 'r', st_hr, st_open, st_start, st_attribute, st_badattr },
+ { 'e', st_hre, st_open, st_start, st_attribute, st_badattr },
+ { 'f', st_href, st_open, st_start, st_attribute, st_badattr },
+ { '=', st_hrefeq, st_open, st_start, st_attribute, st_badattr },
+ { '\"', st_hrefqu, st_open, st_start, st_attribute, st_hrefeq },
+ { '\"', st_attribute, st_hrefqu, st_hrefqu, st_hrefqu, st_hrefqu },
+ { '\"', st_badtagqu, st_open, st_start, st_badtag, st_badtag },
+ { '\"', st_badtag, st_badtagqu, st_badtagqu, st_badtagqu, st_badtagqu },
+ { '\"', st_badattrqu, st_open, st_start, st_attribute, st_badattr },
+ { '\"', st_attribute, st_badattrqu, st_badattrqu, st_badattrqu, st_badattrqu },
+};
+
+struct html_entity {
+ uint16_t ucs;
+ const char entity[9];
+};
+
+static const struct html_entity entities[] = {
+ { 34, "quot" },
+ { 38, "amp" },
+ { 60, "lt" },
+ { 62, "gt" },
+#ifdef HTTP_ALL_ENTITIES
+ { 160, "nbsp" },
+ { 161, "iexcl" },
+ { 162, "cent" },
+ { 163, "pound" },
+ { 164, "curren" },
+ { 165, "yen" },
+ { 166, "brvbar" },
+ { 167, "sect" },
+ { 168, "uml" },
+ { 169, "copy" },
+ { 170, "ordf" },
+ { 171, "laquo" },
+ { 172, "not" },
+ { 173, "shy" },
+ { 174, "reg" },
+ { 175, "macr" },
+ { 176, "deg" },
+ { 177, "plusmn" },
+ { 178, "sup2" },
+ { 179, "sup3" },
+ { 180, "acute" },
+ { 181, "micro" },
+ { 182, "para" },
+ { 183, "middot" },
+ { 184, "cedil" },
+ { 185, "sup1" },
+ { 186, "ordm" },
+ { 187, "raquo" },
+ { 188, "frac14" },
+ { 189, "frac12" },
+ { 190, "frac34" },
+ { 191, "iquest" },
+ { 192, "Agrave" },
+ { 193, "Aacute" },
+ { 194, "Acirc" },
+ { 195, "Atilde" },
+ { 196, "Auml" },
+ { 197, "Aring" },
+ { 198, "AElig" },
+ { 199, "Ccedil" },
+ { 200, "Egrave" },
+ { 201, "Eacute" },
+ { 202, "Ecirc" },
+ { 203, "Euml" },
+ { 204, "Igrave" },
+ { 205, "Iacute" },
+ { 206, "Icirc" },
+ { 207, "Iuml" },
+ { 208, "ETH" },
+ { 209, "Ntilde" },
+ { 210, "Ograve" },
+ { 211, "Oacute" },
+ { 212, "Ocirc" },
+ { 213, "Otilde" },
+ { 214, "Ouml" },
+ { 215, "times" },
+ { 216, "Oslash" },
+ { 217, "Ugrave" },
+ { 218, "Uacute" },
+ { 219, "Ucirc" },
+ { 220, "Uuml" },
+ { 221, "Yacute" },
+ { 222, "THORN" },
+ { 223, "szlig" },
+ { 224, "agrave" },
+ { 225, "aacute" },
+ { 226, "acirc" },
+ { 227, "atilde" },
+ { 228, "auml" },
+ { 229, "aring" },
+ { 230, "aelig" },
+ { 231, "ccedil" },
+ { 232, "egrave" },
+ { 233, "eacute" },
+ { 234, "ecirc" },
+ { 235, "euml" },
+ { 236, "igrave" },
+ { 237, "iacute" },
+ { 238, "icirc" },
+ { 239, "iuml" },
+ { 240, "eth" },
+ { 241, "ntilde" },
+ { 242, "ograve" },
+ { 243, "oacute" },
+ { 244, "ocirc" },
+ { 245, "otilde" },
+ { 246, "ouml" },
+ { 247, "divide" },
+ { 248, "oslash" },
+ { 249, "ugrave" },
+ { 250, "uacute" },
+ { 251, "ucirc" },
+ { 252, "uuml" },
+ { 253, "yacute" },
+ { 254, "thorn" },
+ { 255, "yuml" },
+ { 338, "OElig" },
+ { 339, "oelig" },
+ { 352, "Scaron" },
+ { 353, "scaron" },
+ { 376, "Yuml" },
+ { 402, "fnof" },
+ { 710, "circ" },
+ { 732, "tilde" },
+ { 913, "Alpha" },
+ { 914, "Beta" },
+ { 915, "Gamma" },
+ { 916, "Delta" },
+ { 917, "Epsilon" },
+ { 918, "Zeta" },
+ { 919, "Eta" },
+ { 920, "Theta" },
+ { 921, "Iota" },
+ { 922, "Kappa" },
+ { 923, "Lambda" },
+ { 924, "Mu" },
+ { 925, "Nu" },
+ { 926, "Xi" },
+ { 927, "Omicron" },
+ { 928, "Pi" },
+ { 929, "Rho" },
+ { 931, "Sigma" },
+ { 932, "Tau" },
+ { 933, "Upsilon" },
+ { 934, "Phi" },
+ { 935, "Chi" },
+ { 936, "Psi" },
+ { 937, "Omega" },
+ { 945, "alpha" },
+ { 946, "beta" },
+ { 947, "gamma" },
+ { 948, "delta" },
+ { 949, "epsilon" },
+ { 950, "zeta" },
+ { 951, "eta" },
+ { 952, "theta" },
+ { 953, "iota" },
+ { 954, "kappa" },
+ { 955, "lambda" },
+ { 956, "mu" },
+ { 957, "nu" },
+ { 958, "xi" },
+ { 959, "omicron" },
+ { 960, "pi" },
+ { 961, "rho" },
+ { 962, "sigmaf" },
+ { 963, "sigma" },
+ { 964, "tau" },
+ { 965, "upsilon" },
+ { 966, "phi" },
+ { 967, "chi" },
+ { 968, "psi" },
+ { 969, "omega" },
+ { 977, "thetasym" },
+ { 978, "upsih" },
+ { 982, "piv" },
+ { 8194, "ensp" },
+ { 8195, "emsp" },
+ { 8201, "thinsp" },
+ { 8204, "zwnj" },
+ { 8205, "zwj" },
+ { 8206, "lrm" },
+ { 8207, "rlm" },
+ { 8211, "ndash" },
+ { 8212, "mdash" },
+ { 8216, "lsquo" },
+ { 8217, "rsquo" },
+ { 8218, "sbquo" },
+ { 8220, "ldquo" },
+ { 8221, "rdquo" },
+ { 8222, "bdquo" },
+ { 8224, "dagger" },
+ { 8225, "Dagger" },
+ { 8226, "bull" },
+ { 8230, "hellip" },
+ { 8240, "permil" },
+ { 8242, "prime" },
+ { 8243, "Prime" },
+ { 8249, "lsaquo" },
+ { 8250, "rsaquo" },
+ { 8254, "oline" },
+ { 8260, "frasl" },
+ { 8364, "euro" },
+ { 8465, "image" },
+ { 8472, "weierp" },
+ { 8476, "real" },
+ { 8482, "trade" },
+ { 8501, "alefsym" },
+ { 8592, "larr" },
+ { 8593, "uarr" },
+ { 8594, "rarr" },
+ { 8595, "darr" },
+ { 8596, "harr" },
+ { 8629, "crarr" },
+ { 8656, "lArr" },
+ { 8657, "uArr" },
+ { 8658, "rArr" },
+ { 8659, "dArr" },
+ { 8660, "hArr" },
+ { 8704, "forall" },
+ { 8706, "part" },
+ { 8707, "exist" },
+ { 8709, "empty" },
+ { 8711, "nabla" },
+ { 8712, "isin" },
+ { 8713, "notin" },
+ { 8715, "ni" },
+ { 8719, "prod" },
+ { 8721, "sum" },
+ { 8722, "minus" },
+ { 8727, "lowast" },
+ { 8730, "radic" },
+ { 8733, "prop" },
+ { 8734, "infin" },
+ { 8736, "ang" },
+ { 8743, "and" },
+ { 8744, "or" },
+ { 8745, "cap" },
+ { 8746, "cup" },
+ { 8747, "int" },
+ { 8756, "there4" },
+ { 8764, "sim" },
+ { 8773, "cong" },
+ { 8776, "asymp" },
+ { 8800, "ne" },
+ { 8801, "equiv" },
+ { 8804, "le" },
+ { 8805, "ge" },
+ { 8834, "sub" },
+ { 8835, "sup" },
+ { 8836, "nsub" },
+ { 8838, "sube" },
+ { 8839, "supe" },
+ { 8853, "oplus" },
+ { 8855, "otimes" },
+ { 8869, "perp" },
+ { 8901, "sdot" },
+ { 8968, "lceil" },
+ { 8969, "rceil" },
+ { 8970, "lfloor" },
+ { 8971, "rfloor" },
+ { 9001, "lang" },
+ { 9002, "rang" },
+ { 9674, "loz" },
+ { 9824, "spades" },
+ { 9827, "clubs" },
+ { 9829, "hearts" },
+ { 9830, "diams" },
+#endif /* HTTP_ALL_ENTITIES */
+ { 0, "" }
+};
+
+struct entity_state {
+ char entity_buf[16];
+ char *ep;
+};
+
+static char *emit(char *p, int c, struct entity_state *st)
+{
+ const struct html_entity *ent;
+ unsigned int ucs;
+
+ if (!st->ep) {
+ if (c == '&') {
+ /* Entity open */
+ st->ep = st->entity_buf;
+ } else {
+ *p++ = c;
+ }
+ } else {
+ if (c == ';') {
+ st->ep = NULL;
+ *p = '\0';
+ if (st->entity_buf[0] == '#') {
+ if ((st->entity_buf[1] | 0x20)== 'x') {
+ ucs = strtoul(st->entity_buf + 2, NULL, 16);
+ } else {
+ ucs = strtoul(st->entity_buf + 1, NULL, 10);
+ }
+ } else {
+ for (ent = entities; ent->ucs; ent++) {
+ if (!strcmp(st->entity_buf, ent->entity))
+ break;
+ }
+ ucs = ent->ucs;
+ }
+ if (ucs < 32 || ucs >= 0x10ffff)
+ return p; /* Bogus */
+ if (ucs >= 0x10000) {
+ *p++ = 0xf0 + (ucs >> 18);
+ *p++ = 0x80 + ((ucs >> 12) & 0x3f);
+ *p++ = 0x80 + ((ucs >> 6) & 0x3f);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else if (ucs >= 0x800) {
+ *p++ = 0xe0 + (ucs >> 12);
+ *p++ = 0x80 + ((ucs >> 6) & 0x3f);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else if (ucs >= 0x80) {
+ *p++ = 0xc0 + (ucs >> 6);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else {
+ *p++ = ucs;
+ }
+ } else if (st->ep < st->entity_buf + sizeof st->entity_buf - 1) {
+ *st->ep++ = c;
+ }
+ }
+ return p;
+}
+
+static const char *http_get_filename(struct inode *inode, char *buf)
+{
+ int c, lc;
+ char *p;
+ const struct machine *sm;
+ struct entity_state es;
+ enum http_readdir_state state = st_start;
+ enum http_readdir_state pstate = st_start;
+
+ memset(&es, 0, sizeof es);
+
+ p = buf;
+ for (;;) {
+ c = pxe_getc(inode);
+ if (c == -1)
+ return NULL;
+
+ lc = tolower(c);
+
+ sm = &statemachine[state];
+
+ if (lc == sm->xchar)
+ state = sm->st_xchar;
+ else if (c == '<')
+ state = sm->st_left;
+ else if (c == '>')
+ state = sm->st_right;
+ else if (isspace(c))
+ state = sm->st_space;
+ else
+ state = sm->st_other;
+
+ if (state == st_hrefeq || state == st_hrefqu) {
+ if (state != pstate)
+ p = buf;
+ else if (p < buf + FILENAME_MAX)
+ p = emit(p, c, &es);
+ pstate = state;
+ } else {
+ if (pstate != st_start)
+ pstate = st_start;
+ if (p != buf && state == st_start) {
+ *p = '\0';
+ return buf;
+ }
+ }
+ }
+}
+
+int http_readdir(struct inode *inode, struct dirent *dirent)
+{
+ char buf[FILENAME_MAX + 6];
+ const char *fn, *sp;
+
+ for (;;) {
+ fn = http_get_filename(inode, buf);
+
+ if (!fn)
+ return -1; /* End of directory */
+
+ /* Ignore entries with http special characters */
+ if (strchr(fn, '#'))
+ continue;
+ if (strchr(fn, '?'))
+ continue;
+
+ /* A slash if present has to be the last character, and not the first */
+ sp = strchr(fn, '/');
+ if (sp) {
+ if (sp == fn || sp[1])
+ continue;
+ } else {
+ sp = strchr(fn, '\0');
+ }
+
+ if (sp > fn + NAME_MAX)
+ continue;
+
+ dirent->d_ino = 0; /* Not applicable */
+ dirent->d_off = 0; /* Not applicable */
+ dirent->d_reclen = offsetof(struct dirent, d_name) + (sp-fn) + 1;
+ dirent->d_type = *sp == '/' ? DT_DIR : DT_REG;
+ memcpy(dirent->d_name, fn, sp-fn);
+ dirent->d_name[sp-fn] = '\0';
+ return 0;
+ }
+}
diff --git a/core/fs/pxe/idle.c b/core/fs/pxe/idle.c
index 52a87c34..1d1bb8bc 100644
--- a/core/fs/pxe/idle.c
+++ b/core/fs/pxe/idle.c
@@ -19,91 +19,8 @@
#include <sys/cpu.h>
#include "pxe.h"
-static int pxe_idle_poll(void)
-{
- static __lowmem char junk_pkt[PKTBUF_SIZE];
- static __lowmem t_PXENV_UDP_READ read_buf;
-
- memset(&read_buf, 0, sizeof read_buf);
-
- read_buf.src_ip = 0; /* Any destination */
- read_buf.dest_ip = IPInfo.myip;
- read_buf.s_port = 0; /* Any source port */
- read_buf.d_port = htons(9); /* Discard port (not used...) */
- read_buf.buffer_size = sizeof junk_pkt;
- read_buf.buffer = FAR_PTR(junk_pkt);
-
- pxe_call(PXENV_UDP_READ, &read_buf);
-
- return 0;
-}
-
-static uint32_t pxe_detect_nic_type(void)
-{
- static __lowmem t_PXENV_UNDI_GET_NIC_TYPE nic_type;
-
- if (pxe_call(PXENV_UNDI_GET_NIC_TYPE, &nic_type))
- return -1; /* Unknown NIC */
-
- if (nic_type.NicType != PCI_NIC && nic_type.NicType != CardBus_NIC)
- return -1; /* Not a PCI NIC */
-
- /*
- * Return VID:DID as a single number, with the VID in the high word
- * -- this is opposite from the usual order, but it makes it easier to
- * enforce that the table is sorted.
- */
- return (nic_type.info.pci.Vendor_ID << 16) + nic_type.info.pci.Dev_ID;
-}
-
-#define PCI_DEV(vid, did) (((vid) << 16) + (did))
-
-/* This array should be sorted!! */
-static const uint32_t pxe_need_idle_drain[] =
-{
- /*
- * Older Broadcom NICs: they need receive calls on idle to avoid
- * FIFO stalls.
- */
- PCI_DEV(0x14e4, 0x1659), /* BCM5721 */
- PCI_DEV(0x14e4, 0x165a), /* BCM5722 */
- PCI_DEV(0x14e4, 0x165b), /* BCM5723 */
- PCI_DEV(0x14e4, 0x1668), /* BCM5714 */
- PCI_DEV(0x14e4, 0x1669), /* BCM5714S */
- PCI_DEV(0x14e4, 0x166a), /* BCM5780 */
- PCI_DEV(0x14e4, 0x1673), /* BCM5755M */
- PCI_DEV(0x14e4, 0x1674), /* BCM5756ME */
- PCI_DEV(0x14e4, 0x1678), /* BCM5715 */
- PCI_DEV(0x14e4, 0x1679), /* BCM5715S */
- PCI_DEV(0x14e4, 0x167b), /* BCM5755 */
-};
-
void pxe_idle_init(void)
{
- uint32_t dev_id = pxe_detect_nic_type();
- int l, h;
- bool found;
-
- l = 0;
- h = sizeof pxe_need_idle_drain / sizeof pxe_need_idle_drain[0] - 1;
-
- found = false;
- while (h >= l) {
- int x = (l+h) >> 1;
- uint32_t id = pxe_need_idle_drain[x];
-
- if (id == dev_id) {
- found = true;
- break;
- } else if (id < dev_id) {
- l = x+1;
- } else {
- h = x-1;
- }
- }
-
- if (found)
- idle_hook_func = pxe_idle_poll;
}
void pxe_idle_cleanup(void)
diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c
new file mode 100644
index 00000000..d0a0bf90
--- /dev/null
+++ b/core/fs/pxe/isr.c
@@ -0,0 +1,298 @@
+/*
+ * core/fs/pxe/isr.c
+ *
+ * Stub invoked on return from real mode including from an interrupt.
+ * Interrupts are locked out on entry.
+ */
+
+#include "core.h"
+#include "thread.h"
+#include "pxe.h"
+#include <string.h>
+#include <sys/cpu.h>
+#include <sys/io.h>
+
+extern uint8_t pxe_irq_pending;
+extern volatile uint8_t pxe_need_poll;
+static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0);
+static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0);
+static struct thread *pxe_thread, *poll_thread;
+
+#ifndef PXE_POLL_FORCE
+# define PXE_POLL_FORCE 0
+#endif
+
+#ifndef PXE_POLL_BY_MODEL
+# define PXE_POLL_BY_MODEL 1
+#endif
+
+/*
+ * Note: this *must* be called with interrupts enabled.
+ */
+static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
+{
+ far_ptr_t *entry;
+ unsigned int vec;
+ uint8_t mask, mymask;
+ uint32_t now;
+ bool ok;
+
+ if (irq < 8)
+ vec = irq + 0x08;
+ else if (irq < 16)
+ vec = (irq - 8) + 0x70;
+ else
+ return false;
+
+ cli();
+
+ if (pxe_need_poll) {
+ sti();
+ return false;
+ }
+
+ entry = (far_ptr_t *)(vec << 2);
+ *old = *entry;
+ entry->ptr = (uint32_t)isr;
+
+ /* Enable this interrupt at the PIC level, just in case... */
+ mymask = ~(1 << (irq & 7));
+ if (irq >= 8) {
+ mask = inb(0x21);
+ mask &= ~(1 << 2); /* Enable cascade */
+ outb(mask, 0x21);
+ mask = inb(0xa1);
+ mask &= mymask;
+ outb(mask, 0xa1);
+ } else {
+ mask = inb(0x21);
+ mask &= mymask;
+ outb(mask, 0x21);
+ }
+
+ sti();
+
+ now = jiffies();
+
+ /* Some time to watch for stuck interrupts */
+ while (jiffies() - now < 4 && (ok = !pxe_need_poll))
+ hlt();
+
+ if (!ok)
+ *entry = *old; /* Restore the old vector */
+
+ ddprintf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec,
+ old->seg, old->offs, entry->seg, entry->offs);
+
+ return ok;
+}
+
+static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
+{
+ far_ptr_t *entry;
+ unsigned int vec;
+ bool rv;
+
+ if (!irq)
+ return true; /* Nothing to uninstall */
+
+ if (irq < 8)
+ vec = irq + 0x08;
+ else if (irq < 16)
+ vec = (irq - 8) + 0x70;
+ else
+ return false;
+
+ cli();
+
+ entry = (far_ptr_t *)(vec << 2);
+
+ if (entry->ptr != (uint32_t)isr) {
+ rv = false;
+ } else {
+ *entry = *old;
+ rv = true;
+ }
+
+ sti();
+ return rv;
+}
+
+static void pxe_poll_wakeups(void)
+{
+ static jiffies_t last_jiffies = 0;
+ jiffies_t now = jiffies();
+
+ if (pxe_need_poll == 1) {
+ /* If we need polling now, activate polling */
+ pxe_need_poll = 3;
+ sem_up(&pxe_poll_thread_sem);
+ }
+
+ if (now != last_jiffies) {
+ last_jiffies = now;
+ __thread_process_timeouts();
+ }
+
+ if (pxe_irq_pending) {
+ pxe_irq_pending = 0;
+ sem_up(&pxe_receive_thread_sem);
+ }
+}
+
+static void pxe_process_irq(void)
+{
+ static __lowmem t_PXENV_UNDI_ISR isr;
+
+ uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */
+ bool done = false;
+
+ while (!done) {
+ memset(&isr, 0, sizeof isr);
+ isr.FuncFlag = func;
+ func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */
+
+ pxe_call(PXENV_UNDI_ISR, &isr);
+
+ switch (isr.FuncFlag) {
+ case PXENV_UNDI_ISR_OUT_DONE:
+ done = true;
+ break;
+
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* Transmit complete - nothing for us to do */
+ break;
+
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ undiif_input(&isr);
+ break;
+
+ case PXENV_UNDI_ISR_OUT_BUSY:
+ /* ISR busy, this should not happen */
+ done = true;
+ break;
+
+ default:
+ /* Invalid return code, this should not happen */
+ done = true;
+ break;
+ }
+ }
+}
+
+static void pxe_receive_thread(void *dummy)
+{
+ (void)dummy;
+
+ for (;;) {
+ sem_down(&pxe_receive_thread_sem, 0);
+ pxe_process_irq();
+ }
+}
+
+static bool pxe_isr_poll(void)
+{
+ static __lowmem t_PXENV_UNDI_ISR isr;
+
+ isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+ pxe_call(PXENV_UNDI_ISR, &isr);
+
+ return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS;
+}
+
+static void pxe_poll_thread(void *dummy)
+{
+ (void)dummy;
+
+ /* Block indefinitely unless activated */
+ sem_down(&pxe_poll_thread_sem, 0);
+
+ for (;;) {
+ cli();
+ if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll())
+ sem_up(&pxe_receive_thread_sem);
+ else
+ __schedule();
+ sti();
+ cpu_relax();
+ }
+}
+
+/*
+ * This does preparations and enables the PXE thread
+ */
+void pxe_init_isr(void)
+{
+ start_idle_thread();
+ sched_hook_func = pxe_poll_wakeups;
+ /*
+ * Run the pxe receive thread at elevated priority, since the UNDI
+ * stack is likely to have very limited memory available; therefore to
+ * avoid packet loss we need to move it into memory that we ourselves
+ * manage, as soon as possible.
+ */
+ core_pm_hook = __schedule;
+
+ pxe_thread = start_thread("pxe receive", 16384, -20,
+ pxe_receive_thread, NULL);
+}
+
+/*
+ * Actually start the interrupt routine inside the UNDI stack
+ */
+void pxe_start_isr(void)
+{
+ int irq = pxe_undi_info.IntNumber;
+
+ if (irq == 2)
+ irq = 9; /* IRQ 2 is really IRQ 9 */
+ else if (irq > 15)
+ irq = 0; /* Invalid IRQ */
+
+ pxe_irq_vector = irq;
+
+ if (irq) {
+ if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain))
+ irq = 0; /* Install failed or stuck interrupt */
+ }
+
+ poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY,
+ pxe_poll_thread, NULL);
+
+ if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) {
+ asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
+ dprintf("pxe_start_isr: forcing pxe_need_poll\n");
+ } else if (PXE_POLL_BY_MODEL) {
+ dprintf("pxe_start_isr: trying poll by model\n");
+ int hwad = ((int)MAC[0] << 16) + ((int)MAC[1] << 8) + MAC[2];
+ dprintf("pxe_start_isr: got %06x %04x\n", hwad, pxe_undi_iface.ServiceFlags);
+ if (hwad == 0x000023ae) {
+ if (pxe_undi_iface.ServiceFlags == 0xdc1b) {
+ asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
+ dprintf("pxe_start_isr: forcing pxe_need_poll by model\n");
+ }
+ }
+ }
+}
+
+int reset_pxe(void)
+{
+ static __lowmem struct s_PXENV_UNDI_CLOSE undi_close;
+
+ sched_hook_func = NULL;
+ core_pm_hook = core_pm_null_hook;
+ kill_thread(pxe_thread);
+
+ memset(&undi_close, 0, sizeof(undi_close));
+ pxe_call(PXENV_UNDI_CLOSE, &undi_close);
+
+ if (undi_close.Status)
+ printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status);
+
+ if (pxe_irq_vector)
+ uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
+ if (poll_thread)
+ kill_thread(poll_thread);
+
+ return undi_close.Status;
+}
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index 21d27e5c..526323a4 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -2,12 +2,19 @@
#include <stdio.h>
#include <string.h>
#include <core.h>
+#include <bios.h>
#include <fs.h>
#include <minmax.h>
+#include <fcntl.h>
#include <sys/cpu.h>
#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+#include "tftp.h"
+#include <net.h>
-#define GPXE 1
+__lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
+__lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
static uint16_t real_base_mem; /* Amount of DOS memory after freeing */
@@ -15,46 +22,13 @@ uint8_t MAC[MAC_MAX]; /* Actual MAC address */
uint8_t MAC_len; /* MAC address len */
uint8_t MAC_type; /* MAC address type */
-char __bss16 BOOTIFStr[7+3*(MAC_MAX+1)];
-#define MAC_str (BOOTIFStr+7) /* The actual hardware address */
-char __bss16 SYSUUIDStr[8+32+5];
-#define UUID_str (SYSUUIDStr+8) /* The actual UUID */
-
char boot_file[256]; /* From DHCP */
char path_prefix[256]; /* From DHCP */
-char dot_quad_buf[16];
static bool has_gpxe;
static uint32_t gpxe_funcs;
bool have_uuid = false;
-/* Common receive buffer */
-static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
-
-const uint8_t TimeoutTable[] = {
- 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
- 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
-};
-
-struct tftp_options {
- const char *str_ptr; /* string pointer */
- size_t offset; /* offset into socket structre */
-};
-
-#define IFIELD(x) offsetof(struct inode, x)
-#define PFIELD(x) (offsetof(struct inode, pvt) + \
- offsetof(struct pxe_pvt_inode, x))
-
-static const struct tftp_options tftp_options[] =
-{
- { "tsize", IFIELD(size) },
- { "blksize", PFIELD(tftp_blksize) },
-};
-static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
-
-static void tftp_error(struct inode *file, uint16_t errnum,
- const char *errstr);
-
/*
* Allocate a local UDP port structure and assign it a local port number.
* Return the inode pointer if success, or null if failure
@@ -66,108 +40,35 @@ static struct inode *allocate_socket(struct fs_info *fs)
if (!inode) {
malloc_error("socket structure");
} else {
- struct pxe_pvt_inode *socket = PVT(inode);
- socket->tftp_localport = get_port();
inode->mode = DT_REG; /* No other types relevant for PXE */
}
return inode;
}
-static void free_socket(struct inode *inode)
+void free_socket(struct inode *inode)
{
struct pxe_pvt_inode *socket = PVT(inode);
- free_port(socket->tftp_localport);
+ free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */
free_inode(inode);
}
-#if GPXE
-static void gpxe_close_file(struct inode *inode)
-{
- struct pxe_pvt_inode *socket = PVT(inode);
- static __lowmem struct s_PXENV_FILE_CLOSE file_close;
-
- file_close.FileHandle = socket->tftp_remoteport;
- pxe_call(PXENV_FILE_CLOSE, &file_close);
-}
-#endif
-
static void pxe_close_file(struct file *file)
{
struct inode *inode = file->inode;
struct pxe_pvt_inode *socket = PVT(inode);
+ if (!inode)
+ return;
+
if (!socket->tftp_goteof) {
-#if GPXE
- if (socket->tftp_localport == 0xffff) {
- gpxe_close_file(inode);
- } else
-#endif
- if (socket->tftp_localport != 0) {
- tftp_error(inode, 0, "No error, file close");
- }
+ socket->ops->close(inode);
}
free_socket(inode);
}
-/**
- * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
- *
- * @param: dst, output buffer
- * @param: src, input buffer
- * @param: count, number of bytes
- *
- */
-static void lchexbytes(char *dst, const void *src, int count)
-{
- uint8_t half;
- uint8_t c;
- const uint8_t *s = src;
-
- for(; count > 0; count--) {
- c = *s++;
- half = ((c >> 4) & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
-
- half = (c & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
- }
-}
-
-/*
- * just like the lchexbytes, except to upper-case
- *
- */
-static void uchexbytes(char *dst, const void *src, int count)
-{
- uint8_t half;
- uint8_t c;
- const uint8_t *s = src;
-
- for(; count > 0; count--) {
- c = *s++;
- half = ((c >> 4) & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
-
- half = (c & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
- }
-}
-
-/*
- * Parse a single hexadecimal byte, which must be complete (two
- * digits). This is used in URL parsing.
- */
-static int hexbyte(const char *p)
-{
- if (!is_hex(p[0]) || !is_hex(p[1]))
- return -1;
- else
- return (hexval(p[0]) << 4) + hexval(p[1]);
-}
-
/*
* Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
* We used to refuse class E, but class E addresses are likely to become
@@ -196,70 +97,27 @@ bool ip_ok(uint32_t ip)
*/
static int gendotquad(char *dst, uint32_t ip)
{
- int part;
- int i = 0, j;
- char temp[4];
- char *p = dst;
-
- for (; i < 4; i++) {
- j = 0;
- part = ip & 0xff;
- do {
- temp[j++] = (part % 10) + '0';
- }while(part /= 10);
- for (; j > 0; j--)
- *p++ = temp[j-1];
- *p++ = '.';
-
- ip >>= 8;
- }
- /* drop the last dot '.' and zero-terminate string*/
- *(--p) = 0;
-
- return p - dst;
-}
-
-/*
- * parse the ip_str and return the ip address with *res.
- * return the the string address after the ip string
- *
- */
-static const char *parse_dotquad(const char *ip_str, uint32_t *res)
-{
- const char *p = ip_str;
- uint8_t part = 0;
- uint32_t ip = 0;
- int i;
-
- for (i = 0; i < 4; i++) {
- while (is_digit(*p)) {
- part = part * 10 + *p - '0';
- p++;
- }
- if (i != 3 && *p != '.')
- return NULL;
-
- ip = (ip << 8) | part;
- part = 0;
- p++;
- }
- p--;
-
- *res = htonl(ip);
- return p;
+ return sprintf(dst, "%u.%u.%u.%u",
+ ((const uint8_t *)&ip)[0],
+ ((const uint8_t *)&ip)[1],
+ ((const uint8_t *)&ip)[2],
+ ((const uint8_t *)&ip)[3]);
}
/*
* the ASM pxenv function wrapper, return 1 if error, or 0
*
*/
-int pxe_call(int opcode, void *data)
+__export int pxe_call(int opcode, void *data)
{
+ static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
extern void pxenv(void);
com32sys_t regs;
+ sem_down(&pxe_sem, 0);
+
#if 0
- printf("pxe_call op %04x data %p\n", opcode, data);
+ dprintf("pxe_call op %04x data %p\n", opcode, data);
#endif
memset(&regs, 0, sizeof regs);
@@ -268,201 +126,44 @@ int pxe_call(int opcode, void *data)
regs.edi.w[0] = OFFS(data);
call16(pxenv, &regs, &regs);
- return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
-}
-
-/**
- * Send an ERROR packet. This is used to terminate a connection.
- *
- * @inode: Inode structure
- * @errnum: Error number (network byte order)
- * @errstr: Error string (included in packet)
- */
-static void tftp_error(struct inode *inode, uint16_t errnum,
- const char *errstr)
-{
- static __lowmem struct {
- uint16_t err_op;
- uint16_t err_num;
- char err_msg[64];
- } __packed err_buf;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
- struct pxe_pvt_inode *socket = PVT(inode);
-
- err_buf.err_op = TFTP_ERROR;
- err_buf.err_num = errnum;
- memcpy(err_buf.err_msg, errstr, len);
- err_buf.err_msg[len] = '\0';
-
- udp_write.src_port = socket->tftp_localport;
- udp_write.dst_port = socket->tftp_remoteport;
- udp_write.ip = socket->tftp_remoteip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.buffer = FAR_PTR(&err_buf);
- udp_write.buffer_size = 4 + len + 1;
+ sem_up(&pxe_sem);
- /* If something goes wrong, there is nothing we can do, anyway... */
- pxe_call(PXENV_UDP_WRITE, &udp_write);
-}
-
-
-/**
- * Send ACK packet. This is a common operation and so is worth canning.
- *
- * @param: inode, Inode pointer
- * @param: ack_num, Packet # to ack (network byte order)
- *
- */
-static void ack_packet(struct inode *inode, uint16_t ack_num)
-{
- int err;
- static __lowmem uint16_t ack_packet_buf[2];
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- struct pxe_pvt_inode *socket = PVT(inode);
-
- /* Packet number to ack */
- ack_packet_buf[0] = TFTP_ACK;
- ack_packet_buf[1] = ack_num;
- udp_write.src_port = socket->tftp_localport;
- udp_write.dst_port = socket->tftp_remoteport;
- udp_write.ip = socket->tftp_remoteip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.buffer = FAR_PTR(ack_packet_buf);
- udp_write.buffer_size = 4;
-
- err = pxe_call(PXENV_UDP_WRITE, &udp_write);
- (void)err;
-#if 0
- printf("sent %s\n", err ? "FAILED" : "OK");
-#endif
+ return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
}
-
/**
- * Get a DHCP packet from the PXE stack into the trackbuf
+ * Get a DHCP packet from the PXE stack into a lowmem buffer
*
* @param: type, packet type
* @return: buffer size
*
*/
-static int pxe_get_cached_info(int type)
+static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
{
int err;
static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
- printf(" %02x", type);
+ ddprintf(" %02x", type);
- get_cached_info.Status = 0;
+ memset(&get_cached_info, 0, sizeof get_cached_info);
get_cached_info.PacketType = type;
- get_cached_info.BufferSize = 8192;
- get_cached_info.Buffer = FAR_PTR(trackbuf);
+ get_cached_info.BufferSize = bufsiz;
+ get_cached_info.Buffer = FAR_PTR(buf);
err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
if (err) {
- printf("PXE API call failed, error %04x\n", err);
+ ddprintf("PXE API call failed, error %04x\n", err);
kaboom();
}
return get_cached_info.BufferSize;
}
-
-/*
- * Return the type of pathname passed.
- */
-enum pxe_path_type {
- PXE_RELATIVE, /* No :: or URL */
- PXE_HOMESERVER, /* Starting with :: */
- PXE_TFTP, /* host:: */
- PXE_URL_TFTP, /* tftp:// */
- PXE_URL, /* Absolute URL syntax */
-};
-
-static enum pxe_path_type pxe_path_type(const char *str)
-{
- const char *p;
-
- p = str;
-
- while (1) {
- switch (*p) {
- case ':':
- if (p[1] == ':') {
- if (p == str)
- return PXE_HOMESERVER;
- else
- return PXE_TFTP;
- } else if (p > str && p[1] == '/' && p[2] == '/') {
- if (!strncasecmp(str, "tftp://", 7))
- return PXE_URL_TFTP;
- else
- return PXE_URL;
- }
-
- /* else fall through */
- case '/': case '!': case '@': case '#': case '%':
- case '^': case '&': case '*': case '(': case ')':
- case '[': case ']': case '{': case '}': case '\\':
- case '|': case '=': case '`': case '~': case '\'':
- case '\"': case ';': case '>': case '<': case '?':
- case '\0':
- /* Any of these characters terminate the colon search */
- return PXE_RELATIVE;
- default:
- break;
- }
- p++;
- }
-}
-
-#if GPXE
-
-/**
- * Get a fresh packet from a gPXE socket
- * @param: inode -> Inode pointer
- *
- */
-static void get_packet_gpxe(struct inode *inode)
-{
- struct pxe_pvt_inode *socket = PVT(inode);
- static __lowmem struct s_PXENV_FILE_READ file_read;
- int err;
-
- while (1) {
- file_read.FileHandle = socket->tftp_remoteport;
- file_read.Buffer = FAR_PTR(packet_buf);
- file_read.BufferSize = PKTBUF_SIZE;
- err = pxe_call(PXENV_FILE_READ, &file_read);
- if (!err) /* successed */
- break;
-
- if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
- kaboom();
- }
-
- memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
-
- socket->tftp_dataptr = socket->tftp_pktbuf;
- socket->tftp_bytesleft = file_read.BufferSize;
- socket->tftp_filepos += file_read.BufferSize;
-
- if (socket->tftp_bytesleft == 0)
- inode->size = socket->tftp_filepos;
-
- /* if we're done here, close the file */
- if (inode->size > socket->tftp_filepos)
- return;
-
- /* Got EOF, close it */
- socket->tftp_goteof = 1;
- gpxe_close_file(inode);
-}
-#endif /* GPXE */
-
-
/*
* mangle a filename pointed to by _src_ into a buffer pointed
* to by _dst_; ends on encountering any whitespace.
*
+ * This deliberately does not attempt to do any conversion of
+ * pathname separators.
+ *
*/
static void pxe_mangle_name(char *dst, const char *src)
{
@@ -475,109 +176,40 @@ static void pxe_mangle_name(char *dst, const char *src)
}
/*
- * Get a fresh packet if the buffer is drained, and we haven't hit
- * EOF yet. The buffer should be filled immediately after draining!
+ * Read a single character from the specified pxe inode.
+ * Very useful for stepping through http streams and
+ * parsing their headers.
*/
-static void fill_buffer(struct inode *inode)
+int pxe_getc(struct inode *inode)
{
- int err;
- int last_pkt;
- const uint8_t *timeout_ptr;
- uint8_t timeout;
- uint16_t buffersize;
- uint32_t oldtime;
- void *data = NULL;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
struct pxe_pvt_inode *socket = PVT(inode);
+ unsigned char byte;
- if (socket->tftp_bytesleft || socket->tftp_goteof)
- return;
+ while (!socket->tftp_bytesleft) {
+ if (socket->tftp_goteof)
+ return -1;
-#if GPXE
- if (socket->tftp_localport == 0xffff) {
- get_packet_gpxe(inode);
- return;
+ socket->ops->fill_buffer(inode);
}
-#endif
-
- /*
- * Start by ACKing the previous packet; this should cause
- * the next packet to be sent.
- */
- timeout_ptr = TimeoutTable;
- timeout = *timeout_ptr++;
- oldtime = jiffies();
-
- ack_again:
- ack_packet(inode, socket->tftp_lastpkt);
-
- while (timeout) {
- udp_read.buffer = FAR_PTR(packet_buf);
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.src_ip = socket->tftp_remoteip;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.s_port = socket->tftp_remoteport;
- udp_read.d_port = socket->tftp_localport;
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- if (err) {
- uint32_t now = jiffies();
-
- if (now-oldtime >= timeout) {
- oldtime = now;
- timeout = *timeout_ptr++;
- if (!timeout)
- break;
- }
- continue;
- }
- if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
- continue;
+ byte = *socket->tftp_dataptr;
+ socket->tftp_bytesleft -= 1;
+ socket->tftp_dataptr += 1;
- data = packet_buf;
- if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
- continue;
-
- /* If goes here, recevie OK, break */
- break;
- }
-
- /* time runs out */
- if (timeout == 0)
- kaboom();
+ return byte;
+}
- last_pkt = socket->tftp_lastpkt;
- last_pkt = ntohs(last_pkt); /* Host byte order */
- last_pkt++;
- last_pkt = htons(last_pkt); /* Network byte order */
- if (*(uint16_t *)(data + 2) != last_pkt) {
- /*
- * Wrong packet, ACK the packet and try again.
- * This is presumably because the ACK got lost,
- * so the server just resent the previous packet.
- */
-#if 0
- printf("Wrong packet, wanted %04x, got %04x\n", \
- htons(last_pkt), htons(*(uint16_t *)(data+2)));
-#endif
- goto ack_again;
- }
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void fill_buffer(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ if (socket->tftp_bytesleft || socket->tftp_goteof)
+ return;
- /* It's the packet we want. We're also EOF if the size < blocksize */
- socket->tftp_lastpkt = last_pkt; /* Update last packet number */
- buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */
- memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize);
- socket->tftp_dataptr = socket->tftp_pktbuf;
- socket->tftp_filepos += buffersize;
- socket->tftp_bytesleft = buffersize;
- if (buffersize < socket->tftp_blksize) {
- /* it's the last block, ACK packet immediately */
- ack_packet(inode, *(uint16_t *)(data + 2));
-
- /* Make sure we know we are at end of file */
- inode->size = socket->tftp_filepos;
- socket->tftp_goteof = 1;
- }
+ return socket->ops->fill_buffer(inode);
}
@@ -634,8 +266,20 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
return bytes_read;
}
+/*
+ * Assign an IP address to a URL
+ */
+static void url_set_ip(struct url_info *url)
+{
+ url->ip = 0;
+ if (url->host)
+ url->ip = dns_resolv(url->host);
+ if (!url->ip)
+ url->ip = IPInfo.serverip;
+}
+
/**
- * Open a TFTP connection to the server
+ * Open the specified connection
*
* @param:filename, the file we wanna open
*
@@ -643,341 +287,86 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
* @out: the lenght of this file, stores in file->file_len
*
*/
-static void __pxe_searchdir(const char *filename, struct file *file);
+static void __pxe_searchdir(const char *filename, int flags, struct file *file);
extern uint16_t PXERetry;
-static void pxe_searchdir(const char *filename, struct file *file)
+static void pxe_searchdir(const char *filename, int flags, struct file *file)
{
int i = PXERetry;
do {
dprintf("PXE: file = %p, retries left = %d: ", file, i);
- __pxe_searchdir(filename, file);
+ __pxe_searchdir(filename, flags, file);
dprintf("%s\n", file->inode ? "ok" : "failed");
} while (!file->inode && i--);
}
-
-static void __pxe_searchdir(const char *filename, struct file *file)
+static void __pxe_searchdir(const char *filename, int flags, struct file *file)
{
struct fs_info *fs = file->fs;
struct inode *inode;
- struct pxe_pvt_inode *socket;
- char *buf;
- const char *np;
- char *p;
- char *options;
- char *data;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
- static __lowmem struct s_PXENV_FILE_OPEN file_open;
- static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
- static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
- const struct tftp_options *tftp_opt;
- int i = 0;
- int err;
- int buffersize;
- int rrq_len;
- const uint8_t *timeout_ptr;
- uint32_t timeout;
- uint32_t oldtime;
- uint16_t tid;
- uint16_t opcode;
- uint16_t blk_num;
- uint32_t ip = 0;
- uint32_t opdata, *opdata_ptr;
- enum pxe_path_type path_type;
char fullpath[2*FILENAME_MAX];
- uint16_t server_port = TFTP_PORT; /* TFTP server port */
+#if GPXE
+ char urlsave[2*FILENAME_MAX];
+#endif
+ struct url_info url;
+ const struct url_scheme *us = NULL;
+ int redirect_count = 0;
+ bool found_scheme = false;
inode = file->inode = NULL;
-
- buf = rrq_packet_buf;
- *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
- buf += 2;
-
- path_type = pxe_path_type(filename);
- if (path_type == PXE_RELATIVE) {
- snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
- path_type = pxe_path_type(filename = fullpath);
- }
-
- switch (path_type) {
- case PXE_RELATIVE: /* Really shouldn't happen... */
- case PXE_URL:
- buf = stpcpy(buf, filename);
- ip = IPInfo.serverip; /* Default server */
- break;
-
- case PXE_HOMESERVER:
- buf = stpcpy(buf, filename+2);
- ip = IPInfo.serverip;
- break;
-
- case PXE_TFTP:
- np = strchr(filename, ':');
- buf = stpcpy(buf, np+2);
- if (parse_dotquad(filename, &ip) != np)
- ip = dns_resolv(filename);
- break;
-
- case PXE_URL_TFTP:
- np = filename + 7;
- while (*np && *np != '/' && *np != ':')
- np++;
- if (np > filename + 7) {
- if (parse_dotquad(filename + 7, &ip) != np)
- ip = dns_resolv(filename + 7);
- }
- if (*np == ':') {
- np++;
- server_port = 0;
- while (*np >= '0' && *np <= '9')
- server_port = server_port * 10 + *np++ - '0';
- server_port = server_port ? htons(server_port) : TFTP_PORT;
- }
- if (*np == '/')
- np++; /* Do *NOT* eat more than one slash here... */
- /*
- * The ; is because of a quirk in the TFTP URI spec (RFC
- * 3617); it is to be followed by TFTP modes, which we just ignore.
- */
- while (*np && *np != ';') {
- int v;
- if (*np == '%' && (v = hexbyte(np+1)) > 0) {
- *buf++ = v;
- np += 3;
- } else {
- *buf++ = *np++;
- }
- }
- *buf = '\0';
- break;
- }
-
- buf++; /* Point *past* the final NULL */
- memcpy(buf, rrq_tail, sizeof rrq_tail);
- buf += sizeof rrq_tail;
- rrq_len = buf - rrq_packet_buf;
-
- inode = allocate_socket(fs);
- if (!inode)
- return; /* Allocation failure */
- socket = PVT(inode);
+ while (filename) {
+ if (redirect_count++ > 5)
+ break;
+ strlcpy(fullpath, filename, sizeof fullpath);
#if GPXE
- if (path_type == PXE_URL) {
- if (has_gpxe) {
- file_open.Status = PXENV_STATUS_BAD_FUNC;
- file_open.FileName = FAR_PTR(rrq_packet_buf + 2);
- err = pxe_call(PXENV_FILE_OPEN, &file_open);
- if (err)
- goto done;
-
- socket->tftp_localport = -1;
- socket->tftp_remoteport = file_open.FileHandle;
- inode->size = -1;
- goto done;
- } else {
- static bool already = false;
- if (!already) {
- printf("URL syntax, but gPXE extensions not detected, "
- "trying plain TFTP...\n");
- already = true;
- }
+ strcpy(urlsave, fullpath);
+#endif
+ parse_url(&url, fullpath);
+ if (url.type == URL_SUFFIX) {
+ snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
+#if GPXE
+ strcpy(urlsave, fullpath);
+#endif
+ parse_url(&url, fullpath);
}
- }
-#endif /* GPXE */
-
- if (!ip)
- goto done; /* No server */
-
- timeout_ptr = TimeoutTable; /* Reset timeout */
-
-sendreq:
- timeout = *timeout_ptr++;
- if (!timeout)
- return; /* No file available... */
- oldtime = jiffies();
-
- socket->tftp_remoteip = ip;
- tid = socket->tftp_localport; /* TID(local port No) */
- udp_write.buffer = FAR_PTR(rrq_packet_buf);
- udp_write.ip = ip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.src_port = tid;
- udp_write.dst_port = server_port;
- udp_write.buffer_size = rrq_len;
- pxe_call(PXENV_UDP_WRITE, &udp_write);
-
- /* If the WRITE call fails, we let the timeout take care of it... */
-
-wait_pkt:
- for (;;) {
- buf = packet_buf;
- udp_read.status = 0;
- udp_read.buffer = FAR_PTR(buf);
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.d_port = tid;
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- if (err || udp_read.status) {
- uint32_t now = jiffies();
- if (now - oldtime >= timeout)
- goto sendreq;
- } else {
- /* Make sure the packet actually came from the server */
- if (udp_read.src_ip == socket->tftp_remoteip)
+
+ inode = allocate_socket(fs);
+ if (!inode)
+ return; /* Allocation failure */
+
+ url_set_ip(&url);
+
+ filename = NULL;
+ found_scheme = false;
+ for (us = url_schemes; us->name; us++) {
+ if (!strcmp(us->name, url.scheme)) {
+ if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
+ us->open(&url, flags, inode, &filename);
+ found_scheme = true;
break;
+ }
}
- }
-
- socket->tftp_remoteport = udp_read.s_port;
-
- /* filesize <- -1 == unknown */
- inode->size = -1;
- /* Default blksize unless blksize option negotiated */
- socket->tftp_blksize = TFTP_BLOCKSIZE;
- buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
- if (buffersize < 0)
- goto wait_pkt; /* Garbled reply */
-
- /*
- * Get the opcode type, and parse it
- */
- opcode = *(uint16_t *)packet_buf;
- switch (opcode) {
- case TFTP_ERROR:
- inode->size = 0;
- break; /* ERROR reply; don't try again */
-
- case TFTP_DATA:
- /*
- * If the server doesn't support any options, we'll get a
- * DATA reply instead of OACK. Stash the data in the file
- * buffer and go with the default value for all options...
- *
- * We got a DATA packet, meaning no options are
- * suported. Save the data away and consider the
- * length undefined, *unless* this is the only
- * data packet...
- */
- buffersize -= 2;
- if (buffersize < 0)
- goto wait_pkt;
- data = packet_buf + 2;
- blk_num = *(uint16_t *)data;
- data += 2;
- if (blk_num != htons(1))
- goto wait_pkt;
- socket->tftp_lastpkt = blk_num;
- if (buffersize > TFTP_BLOCKSIZE)
- goto err_reply; /* Corrupt */
- else if (buffersize < TFTP_BLOCKSIZE) {
- /*
- * This is the final EOF packet, already...
- * We know the filesize, but we also want to
- * ack the packet and set the EOF flag.
- */
- inode->size = buffersize;
- socket->tftp_goteof = 1;
- ack_packet(inode, blk_num);
- }
-
- socket->tftp_bytesleft = buffersize;
- socket->tftp_dataptr = socket->tftp_pktbuf;
- memcpy(socket->tftp_pktbuf, data, buffersize);
- break;
-
- case TFTP_OACK:
- /*
- * Now we need to parse the OACK packet to get the transfer
- * and packet sizes.
- */
-
- options = packet_buf + 2;
- p = options;
- while (buffersize) {
- const char *opt = p;
-
- /*
- * If we find an option which starts with a NUL byte,
- * (a null option), we're either seeing garbage that some
- * TFTP servers add to the end of the packet, or we have
- * no clue how to parse the rest of the packet (what is
- * an option name and what is a value?) In either case,
- * discard the rest.
- */
- if (!*opt)
- goto done;
-
- while (buffersize) {
- if (!*p)
- break; /* Found a final null */
- *p++ |= 0x20;
- buffersize--;
- }
- if (!buffersize)
- break; /* Unterminated option */
-
- /* Consume the terminal null */
- p++;
- buffersize--;
-
- if (!buffersize)
- break; /* No option data */
-
- /*
- * Parse option pointed to by options; guaranteed to be
- * null-terminated
- */
- tftp_opt = tftp_options;
- for (i = 0; i < tftp_nopts; i++) {
- if (!strcmp(opt, tftp_opt->str_ptr))
- break;
- tftp_opt++;
- }
- if (i == tftp_nopts)
- goto err_reply; /* Non-negotitated option returned,
- no idea what it means ...*/
-
- /* get the address of the filed that we want to write on */
- opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
- opdata = 0;
-
- /* do convert a number-string to decimal number, just like atoi */
- while (buffersize--) {
- uint8_t d = *p++;
- if (d == '\0')
- break; /* found a final null */
- d -= '0';
- if (d > 9)
- goto err_reply; /* Not a decimal digit */
- opdata = opdata*10 + d;
- }
- *opdata_ptr = opdata;
- }
- break;
+ /* filename here is set on a redirect */
+ }
- default:
- printf("TFTP unknown opcode %d\n", ntohs(opcode));
- goto err_reply;
+ if (!found_scheme) {
+#if GPXE
+ /* No URL scheme found, hand it to GPXE */
+ gpxe_open(inode, urlsave);
+#endif
}
-done:
- if (!inode->size) {
+ if (inode->size) {
+ file->inode = inode;
+ file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
+ } else {
free_socket(inode);
- return;
}
- file->inode = inode;
- return;
-err_reply:
- /* Build the TFTP error packet */
- tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
- printf("TFTP server sent an incomprehesible reply\n");
- kaboom();
+ return;
}
@@ -1014,7 +403,25 @@ static void get_prefix(void)
*(p + 2) = 0; /* Zero-terminate after delimiter */
}
- printf("TFTP prefix: %s\n", path_prefix);
+ ddprintf("TFTP prefix: %s\n", path_prefix);
+
+ if (url_type(path_prefix) == URL_SUFFIX) {
+ /*
+ * Construct a ::-style TFTP path.
+ *
+ * We may have moved out of the root directory at the time
+ * this function is invoked, but to maintain compatibility
+ * with versions of Syslinux < 5.00, path_prefix must be
+ * relative to "::".
+ */
+ p = strdup(path_prefix);
+ if (!p)
+ return;
+
+ snprintf(path_prefix, sizeof path_prefix, "::%s", p);
+ free(p);
+ }
+
chdir(path_prefix);
}
@@ -1024,10 +431,8 @@ static void get_prefix(void)
static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
size_t bufsize)
{
- enum pxe_path_type path_type = pxe_path_type(src);
-
return snprintf(dst, bufsize, "%s%s",
- path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
+ url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
}
/*
@@ -1036,44 +441,26 @@ static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
static int pxe_chdir(struct fs_info *fs, const char *src)
{
/* The cwd for PXE is just a text prefix */
- enum pxe_path_type path_type = pxe_path_type(src);
+ enum url_type path_type = url_type(src);
- if (path_type == PXE_RELATIVE)
+ if (path_type == URL_SUFFIX)
strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
else
strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
+ return 0;
dprintf("cwd = \"%s\"\n", fs->cwd_name);
return 0;
}
- /*
- * try to load a config file, if found, return 1, or return 0
- *
- */
-static int try_load(char *config_name)
+static int pxe_chdir_start(void)
{
- com32sys_t regs;
-
- printf("Trying to load: %-50s ", config_name);
- pxe_mangle_name(KernelName, config_name);
-
- memset(&regs, 0, sizeof regs);
- regs.edi.w[0] = OFFS_WRT(KernelName, 0);
- call16(core_open, &regs, &regs);
- if (regs.eflags.l & EFLAGS_ZF) {
- strcpy(ConfigName, KernelName);
- printf("\r");
- return 0;
- } else {
- printf("ok\n");
- return 1;
- }
+ get_prefix();
+ return 0;
}
-
-/* Load the config file, return 1 if failed, or 0 */
-static int pxe_load_config(void)
+/* Load the config file, return -1 if failed, or 0 */
+static int pxe_open_config(struct com32_filedata *filedata)
{
const char *cfgprefix = "pxelinux.cfg/";
const char *default_str = "default";
@@ -1081,10 +468,10 @@ static int pxe_load_config(void)
char *last;
int tries = 8;
- get_prefix();
+ chdir(path_prefix);
if (DHCPMagic & 0x02) {
/* We got a DHCP option, try it first */
- if (try_load(ConfigName))
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
}
@@ -1094,23 +481,23 @@ static int pxe_load_config(void)
config_file = stpcpy(ConfigName, cfgprefix);
/* Try loading by UUID */
- if (have_uuid) {
- strcpy(config_file, UUID_str);
- if (try_load(ConfigName))
+ if (sysappend_strings[SYSAPPEND_SYSUUID]) {
+ strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
}
/* Try loading by MAC address */
- strcpy(config_file, MAC_str);
- if (try_load(ConfigName))
+ strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
/* Nope, try hexadecimal IP prefixes... */
- uchexbytes(config_file, (uint8_t *)&IPInfo.myip, 4);
+ sprintf(config_file, "%08X", ntohl(IPInfo.myip));
last = &config_file[8];
while (tries) {
*last = '\0'; /* Zero-terminate string */
- if (try_load(ConfigName))
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
last--; /* Drop one character */
tries--;
@@ -1118,10 +505,10 @@ static int pxe_load_config(void)
/* Final attempt: "default" string */
strcpy(config_file, default_str);
- if (try_load(ConfigName))
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
- printf("%-68s\n", "Unable to locate configuration file");
+ ddprintf("%-68s\n", "Unable to locate configuration file");
kaboom();
}
@@ -1130,65 +517,40 @@ static int pxe_load_config(void)
*/
static void make_bootif_string(void)
{
+ static char bootif_str[7+3*(MAC_MAX+1)];
const uint8_t *src;
- char *dst = BOOTIFStr;
+ char *dst = bootif_str;
int i;
dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
src = MAC;
for (i = MAC_len; i; i--)
dst += sprintf(dst, "-%02x", *src++);
-}
-/*
- * Generate the SYSUUID string, if we have one...
- */
-static void make_sysuuid_string(void)
-{
- static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
- const uint8_t *src = uuid;
- const uint8_t *uuid_ptr = uuid_dashes;
- char *dst;
- SYSUUIDStr[0] = '\0'; /* If nothing there... */
-
- /* Try loading by UUID */
- if (have_uuid) {
- dst = stpcpy(SYSUUIDStr, "SYSUUID=");
-
- while (*uuid_ptr) {
- int len = *uuid_ptr;
-
- lchexbytes(dst, src, len);
- dst += len * 2;
- src += len;
- uuid_ptr++;
- *dst++ = '-';
- }
- /* Remove last dash and zero-terminate */
- *--dst = '\0';
- }
+ sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
}
/*
* Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
- * option into IPOption based on a DHCP packet in trackbuf.
+ * option into IPOption based on DHCP information in IPInfo.
*
*/
-char __bss16 IPOption[3+4*16];
-
static void genipopt(void)
{
- char *p = IPOption;
+ static char ip_option[3+4*16];
const uint32_t *v = &IPInfo.myip;
+ char *p;
int i;
- p = stpcpy(p, "ip=");
+ p = stpcpy(ip_option, "ip=");
for (i = 0; i < 4; i++) {
p += gendotquad(p, *v++);
*p++ = ':';
}
*--p = '\0';
+
+ sysappend_strings[SYSAPPEND_IP] = ip_option;
}
@@ -1196,29 +558,13 @@ static void genipopt(void)
static void ip_init(void)
{
uint32_t ip = IPInfo.myip;
+ char dot_quad_buf[16];
genipopt();
gendotquad(dot_quad_buf, ip);
ip = ntohl(ip);
- printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
-}
-
-/*
- * Print the IPAPPEND strings, in order
- */
-extern const uint16_t IPAppends[];
-extern const char numIPAppends[];
-
-static void print_ipappend(void)
-{
- size_t i;
-
- for (i = 0; i < (size_t)numIPAppends; i++) {
- const char *p = (const char *)(size_t)IPAppends[i];
- if (*p)
- printf("%s\n", p);
- }
+ ddprintf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
}
/*
@@ -1291,9 +637,9 @@ static const void *memory_scan(uintptr_t start, int (*func)(const void *))
static const struct pxe_t *memory_scan_for_pxe_struct(void)
{
- extern uint16_t BIOS_fbm; /* Starting segment */
+ uint16_t start = bios_fbm(); /* Starting segment */
- return memory_scan(BIOS_fbm << 10, is_pxe);
+ return memory_scan(start << 10, is_pxe);
}
static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
@@ -1368,13 +714,13 @@ static int pxe_init(bool quiet)
/* Found nothing at all !! */
if (!quiet)
- printf("No !PXE or PXENV+ API found; we're dead...\n");
+ ddprintf("No !PXE or PXENV+ API found; we're dead...\n");
return -1;
have_pxenv:
APIVer = pxenv->version;
if (!quiet)
- printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
+ ddprintf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
/* if the API version number is 0x0201 or higher, use the !PXE structure */
if (APIVer >= 0x201) {
@@ -1411,10 +757,10 @@ static int pxe_init(bool quiet)
have_entrypoint:
if (!quiet) {
- printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
+ ddprintf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
type, PXEEntry.seg, PXEEntry.offs, plan);
- printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
- printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
+ ddprintf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
+ ddprintf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
}
code_seg = code_seg + ((code_len + 15) >> 4);
@@ -1422,6 +768,8 @@ static int pxe_init(bool quiet)
real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
+ probe_undi();
+
return 0;
}
@@ -1446,39 +794,28 @@ static void gpxe_init(void)
}
/*
- * Initialize UDP stack
- *
- */
-static void udp_init(void)
-{
- int err;
- static __lowmem struct s_PXENV_UDP_OPEN udp_open;
- udp_open.src_ip = IPInfo.myip;
- err = pxe_call(PXENV_UDP_OPEN, &udp_open);
- if (err || udp_open.status) {
- printf("Failed to initialize UDP stack ");
- printf("%d\n", udp_open.status);
- kaboom();
- }
-}
-
-
-/*
* Network-specific initialization
*/
static void network_init(void)
{
- struct bootp_t *bp = (struct bootp_t *)trackbuf;
int pkt_len;
+ struct bootp_t *bp;
+ const size_t dhcp_max_packet = 4096;
+
+ bp = lmalloc(dhcp_max_packet);
+ if (!bp) {
+ ddprintf("Out of low memory\n");
+ kaboom();
+ }
*LocalDomain = 0; /* No LocalDomain received */
/*
* Get the DHCP client identifiers (query info 1)
*/
- printf("Getting cached packet ");
- pkt_len = pxe_get_cached_info(1);
- parse_dhcp(pkt_len);
+ ddprintf("Getting cached packet ");
+ pkt_len = pxe_get_cached_info(1, bp, dhcp_max_packet);
+ parse_dhcp(bp, pkt_len);
/*
* We don't use flags from the request packet, so
* this is a good time to initialize DHCPMagic...
@@ -1494,8 +831,8 @@ static void network_init(void)
* Get the BOOTP/DHCP packet that brought us file (and an IP
* address). This lives in the DHCPACK packet (query info 2)
*/
- pkt_len = pxe_get_cached_info(2);
- parse_dhcp(pkt_len);
+ pkt_len = pxe_get_cached_info(2, bp, dhcp_max_packet);
+ parse_dhcp(bp, pkt_len);
/*
* Save away MAC address (assume this is in query info 2. If this
* turns out to be problematic it might be better getting it from
@@ -1509,15 +846,19 @@ static void network_init(void)
* Get the boot file and other info. This lives in the CACHED_REPLY
* packet (query info 3)
*/
- pkt_len = pxe_get_cached_info(3);
- parse_dhcp(pkt_len);
- printf("\n");
+ pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
+ parse_dhcp(bp, pkt_len);
+ ddprintf("\n");
+
+ lfree(bp);
make_bootif_string();
- make_sysuuid_string();
+ /* If DMI and DHCP disagree, which one should we set? */
+ if (have_uuid)
+ sysappend_set_uuid(uuid);
ip_init();
- print_ipappend();
+ /* print_sysappend(); */
/*
* Check to see if we got any PXELINUX-specific DHCP options; in particular,
* if we didn't get the magic enable, do not recognize any other options.
@@ -1525,7 +866,7 @@ static void network_init(void)
if ((DHCPMagic & 1) == 0)
DHCPMagic = 0;
- udp_init();
+ net_core_init();
}
/*
@@ -1536,9 +877,8 @@ static int pxe_fs_init(struct fs_info *fs)
{
(void)fs; /* drop the compile warning message */
- /* This block size is actually arbitrary... */
- fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
- fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
+ /* Prepare for handling pxe interrupts */
+ pxe_init_isr();
/* This block size is actually arbitrary... */
fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
@@ -1600,9 +940,9 @@ static inline bool is_efi(const struct efi_struct *efi)
return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
}
+#if 0
static void install_int18_hack(void)
{
-#if 0
static const uint8_t int18_hack[] =
{
0xcd, 0x18, /* int $0x18 */
@@ -1638,38 +978,22 @@ static void install_int18_hack(void)
*(uint16_t *)(dst+46) = InitStack.seg;
}
}
-#endif
-}
-
-int reset_pxe(void)
-{
- static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
- extern void gpxe_unload(void);
- int err = 0;
-
- pxe_idle_cleanup();
-
- pxe_call(PXENV_UDP_CLOSE, &udp_close);
-
- if (gpxe_funcs & 0x80) {
- /* gPXE special unload implemented */
- call16(gpxe_unload, &zero_regs, NULL);
-
- /* Locate the actual vendor stack... */
- err = pxe_init(true);
- }
-
- install_int18_hack();
- return err;
}
+#endif
/*
* This function unloads the PXE and UNDI stacks and
* unclaims the memory.
*/
-void unload_pxe(void)
+__export void unload_pxe(uint16_t flags)
{
/* PXE unload sequences */
+ /*
+ * iPXE does:
+ * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
+ * Older Syslinux did:
+ * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
+ */
static const uint8_t new_api_unload[] = {
PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
};
@@ -1689,14 +1013,15 @@ void unload_pxe(void)
uint16_t Status; /* All calls have this as the first member */
} unload_call;
- dprintf("FBM before unload = %d\n", BIOS_fbm);
+ dprintf("Called unload_pxe()...\n");
+ dprintf("FBM before unload = %d\n", bios_fbm());
err = reset_pxe();
- dprintf("FBM after reset_pxe = %d, err = %d\n", BIOS_fbm, err);
+ dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err);
/* If we want to keep PXE around, we still need to reset it */
- if (KeepPXE || err)
+ if (flags || err)
return;
dprintf("APIVer = %04x\n", APIVer);
@@ -1707,14 +1032,15 @@ void unload_pxe(void)
memset(&unload_call, 0, sizeof unload_call);
err = pxe_call(api, &unload_call);
if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
- dprintf("PXE unload API call %04x failed\n", api);
+ ddprintf("PXE unload API call %04x failed: 0x%x\n",
+ api, unload_call.Status);
goto cant_free;
}
}
api = 0xff00;
- if (real_base_mem <= BIOS_fbm) { /* Sanity check */
- dprintf("FBM %d < real_base_mem %d\n", BIOS_fbm, real_base_mem);
+ if (real_base_mem <= bios_fbm()) { /* Sanity check */
+ dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem);
goto cant_free;
}
api++;
@@ -1722,23 +1048,34 @@ void unload_pxe(void)
/* Check that PXE actually unhooked the INT 0x1A chain */
int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
int_addr >>= 10;
- if (int_addr >= real_base_mem || int_addr < BIOS_fbm) {
- BIOS_fbm = real_base_mem;
- dprintf("FBM after unload_pxe = %d\n", BIOS_fbm);
+ if (int_addr >= real_base_mem || int_addr < bios_fbm()) {
+ set_bios_fbm(real_base_mem);
+ dprintf("FBM after unload_pxe = %d\n", bios_fbm());
return;
}
dprintf("Can't free FBM, real_base_mem = %d, "
"FBM = %d, INT 1A = %08x (%d)\n",
- real_base_mem, BIOS_fbm,
+ real_base_mem, bios_fbm(),
*(uint32_t *)(4 * 0x1a), int_addr);
cant_free:
- printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
- api, *(uint32_t *)(4 * 0x1a), BIOS_fbm, real_base_mem);
+ ddprintf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
+ api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem);
return;
}
+static int pxe_readdir(struct file *file, struct dirent *dirent)
+{
+ struct inode *inode = file->inode;
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->ops->readdir)
+ return socket->ops->readdir(inode, dirent);
+ else
+ return -1; /* No such operation */
+}
+
const struct fs_ops pxe_fs_ops = {
.fs_name = "pxe",
.fs_flags = FS_NODEV,
@@ -1749,5 +1086,7 @@ const struct fs_ops pxe_fs_ops = {
.getfssec = pxe_getfssec,
.close_file = pxe_close_file,
.mangle_name = pxe_mangle_name,
- .load_config = pxe_load_config,
+ .chdir_start = pxe_chdir_start,
+ .open_config = pxe_open_config,
+ .readdir = pxe_readdir,
};
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index 1e6fa76a..68d4e3e0 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -1,7 +1,7 @@
/* -----------------------------------------------------------------------
*
* Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
- * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,67 +21,22 @@
#define PXE_H
#include <syslinux/pxe_api.h>
-#include "fs.h" /* For MAX_OPEN, should go away */
+#include <fcntl.h> /* For OK_FLAGS_MASK */
+#include "fs.h" /* Mostly for FILENAME_MAX */
/*
* Some basic defines...
*/
-#define TFTP_PORT htons(69) /* Default TFTP port */
-#define TFTP_BLOCKSIZE_LG2 9
-#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
-#define PKTBUF_SIZE 2048 /* */
+#define PKTBUF_SIZE 2048 /* Used mostly by the gPXE backend */
#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
-static inline bool is_hex(char c)
-{
- return (c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'F') ||
- (c >= 'a' && c <= 'f');
-}
-
-static inline int hexval(char c)
-{
- return (c >= 'A') ? (c & ~0x20) - 'A' + 10 : (c - '0');
-}
-
-/*
- * TFTP operation codes
- */
-#define TFTP_RRQ htons(1) // Read rest
-#define TFTP_WRQ htons(2) // Write rest
-#define TFTP_DATA htons(3) // Data packet
-#define TFTP_ACK htons(4) // ACK packet
-#define TFTP_ERROR htons(5) // ERROR packet
-#define TFTP_OACK htons(6) // OACK packet
-
-/*
- * TFTP error codes
- */
-#define TFTP_EUNDEF htons(0) // Unspecified error
-#define TFTP_ENOTFOUND htons(1) // File not found
-#define TFTP_EACCESS htons(2) // Access violation
-#define TFTP_ENOSPACE htons(3) // Disk full
-#define TFTP_EBADOP htons(4) // Invalid TFTP operation
-#define TFTP_EBADID htons(5) // Unknown transfer
-#define TFTP_EEXISTS htons(6) // File exists
-#define TFTP_ENOUSER htons(7) // No such user
-#define TFTP_EOPTNEG htons(8) // Option negotiation failure
-
-
#define BOOTP_OPTION_MAGIC htonl(0x63825363)
#define MAC_MAX 32
-/* Defines for DNS */
-#define DNS_PORT htons(53) /* Default DNS port */
-#define DNS_MAX_PACKET 512 /* Defined by protocol */
-#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */
-
-
/*
- * structures
+ * structures
*/
-
struct pxenv_t {
uint8_t signature[6]; /* PXENV+ */
uint16_t version;
@@ -149,22 +104,42 @@ struct bootp_t {
uint8_t options[1260]; /* Vendor options */
} __attribute__ ((packed));
+struct netconn;
+struct netbuf;
/*
* Our inode private information -- this includes the packet buffer!
*/
+struct pxe_conn_ops {
+ void (*fill_buffer)(struct inode *inode);
+ void (*close)(struct inode *inode);
+ int (*readdir)(struct inode *inode, struct dirent *dirent);
+};
+
+union net_private {
+ struct net_private_lwip {
+ struct netconn *conn; /* lwip network connection */
+ struct netbuf *buf; /* lwip cached buffer */
+ } lwip;
+ struct net_private_tftp {
+ uint32_t remoteip; /* Remote IP address (0 = disconnected) */
+ uint16_t localport; /* Local port number (0=not in use) */
+ } tftp;
+};
+
struct pxe_pvt_inode {
- uint16_t tftp_localport; /* Local port number (0=not in us)*/
- uint16_t tftp_remoteport; /* Remote port number */
- uint32_t tftp_remoteip; /* Remote IP address */
- uint32_t tftp_filepos; /* bytes downloaded (includeing buffer) */
- uint32_t tftp_blksize; /* Block size for this connection(*) */
- uint16_t tftp_bytesleft; /* Unclaimed data bytes */
- uint16_t tftp_lastpkt; /* Sequence number of last packet (NBO) */
- char *tftp_dataptr; /* Pointer to available data */
- uint8_t tftp_goteof; /* 1 if the EOF packet received */
- uint8_t tftp_unused[3]; /* Currently unused */
- char tftp_pktbuf[PKTBUF_SIZE];
-} __attribute__ ((packed));
+ union net_private net; /* Network stack private data */
+ uint16_t tftp_remoteport; /* Remote port number */
+ uint32_t tftp_filepos; /* bytes downloaded (including buffer) */
+ uint32_t tftp_blksize; /* Block size for this connection(*) */
+ uint16_t tftp_bytesleft; /* Unclaimed data bytes */
+ uint16_t tftp_lastpkt; /* Sequence number of last packet (HBO) */
+ char *tftp_dataptr; /* Pointer to available data */
+ uint8_t tftp_goteof; /* 1 if the EOF packet received */
+ uint8_t tftp_unused[3]; /* Currently unused */
+ char *tftp_pktbuf; /* Packet buffer */
+ struct inode *ctl; /* Control connection (for FTP) */
+ const struct pxe_conn_ops *ops;
+};
#define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt))
@@ -184,6 +159,9 @@ struct ip_info {
*/
extern struct ip_info IPInfo;
+extern t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
+extern t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
+
extern uint8_t MAC[];
extern char BOOTIFStr[];
extern uint8_t MAC_len;
@@ -196,9 +174,6 @@ extern char boot_file[];
extern char path_prefix[];
extern char LocalDomain[];
-extern char IPOption[];
-extern char dot_quad_buf[];
-
extern uint32_t dns_server[];
extern uint16_t APIVer;
@@ -211,8 +186,16 @@ extern bool have_uuid;
extern uint8_t uuid_type;
extern uint8_t uuid[];
-extern uint16_t BIOS_fbm;
-extern const uint8_t TimeoutTable[];
+struct url_info;
+struct url_scheme {
+ const char *name;
+ void (*open)(struct url_info *, int, struct inode *, const char **);
+ int ok_flags;
+};
+/* Flags which can be specified in url_scheme.ok_flags */
+#define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY)
+
+extern const struct url_scheme url_schemes[];
/*
* Compute the suitable gateway for a specific route -- too many
@@ -227,26 +210,62 @@ static inline uint32_t gateway(uint32_t ip)
}
/*
- * functions
+ * functions
*/
+/* pxeisr.inc */
+extern uint8_t pxe_irq_vector;
+extern void pxe_isr(void);
+extern far_ptr_t pxe_irq_chain;
+extern void pxe_poll(void);
+
+/* isr.c */
+void pxe_init_isr(void);
+void pxe_start_isr(void);
+int reset_pxe(void);
+
/* pxe.c */
+struct url_info;
bool ip_ok(uint32_t);
-int pxe_call(int, void *);
+int pxe_getc(struct inode *inode);
+void free_socket(struct inode *inode);
-/* dhcp_options.c */
-void parse_dhcp(int);
+/* undiif.c */
+int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw);
+void undiif_input(t_PXENV_UNDI_ISR *isr);
-/* dnsresolv.c */
-int dns_mangle(char **, const char *);
-uint32_t dns_resolv(const char *);
+/* dhcp_options.c */
+void parse_dhcp(const void *, size_t);
/* idle.c */
void pxe_idle_init(void);
void pxe_idle_cleanup(void);
-/* socknum.c */
-uint16_t get_port(void);
-void free_port(uint16_t port);
+/* tftp.c */
+void tftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* gpxeurl.c */
+void gpxe_open(struct inode *inode, const char *url);
+#define GPXE 0
+
+/* http.c */
+void http_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* http_readdir.c */
+int http_readdir(struct inode *inode, struct dirent *dirent);
+
+/* ftp.c */
+void ftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* ftp_readdir.c */
+int ftp_readdir(struct inode *inode, struct dirent *dirent);
+
+/* tcp.c */
+void tcp_close_file(struct inode *inode);
+void tcp_fill_buffer(struct inode *inode);
+const struct pxe_conn_ops tcp_conn_ops;
#endif /* pxe.h */
diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c
new file mode 100644
index 00000000..90761978
--- /dev/null
+++ b/core/fs/pxe/tcp.c
@@ -0,0 +1,78 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tcp.c
+ *
+ * Common operations for TCP-based network protocols
+ */
+
+#include <lwip/api.h>
+#include "pxe.h"
+#include "../../../version.h"
+#include "url.h"
+
+void tcp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->net.lwip.conn) {
+ netconn_delete(socket->net.lwip.conn);
+ socket->net.lwip.conn = NULL;
+ }
+ if (socket->net.lwip.buf) {
+ netbuf_delete(socket->net.lwip.buf);
+ socket->net.lwip.buf = NULL;
+ }
+}
+
+void tcp_fill_buffer(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ void *data;
+ u16_t len;
+ err_t err;
+
+ /* Clean up or advance an inuse netbuf */
+ if (socket->net.lwip.buf) {
+ if (netbuf_next(socket->net.lwip.buf) < 0) {
+ netbuf_delete(socket->net.lwip.buf);
+ socket->net.lwip.buf = NULL;
+ }
+ }
+ /* If needed get a new netbuf */
+ if (!socket->net.lwip.buf) {
+ err = netconn_recv(socket->net.lwip.conn, &(socket->net.lwip.buf));
+ if (!socket->net.lwip.buf || err) {
+ socket->tftp_goteof = 1;
+ if (inode->size == -1)
+ inode->size = socket->tftp_filepos;
+ socket->ops->close(inode);
+ return;
+ }
+ }
+ /* Report the current fragment of the netbuf */
+ err = netbuf_data(socket->net.lwip.buf, &data, &len);
+ if (err) {
+ printf("netbuf_data err: %d\n", err);
+ kaboom();
+ }
+ socket->tftp_dataptr = data;
+ socket->tftp_filepos += len;
+ socket->tftp_bytesleft = len;
+ return;
+}
+
+const struct pxe_conn_ops tcp_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = tcp_close_file,
+};
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
new file mode 100644
index 00000000..7af14858
--- /dev/null
+++ b/core/fs/pxe/tftp.c
@@ -0,0 +1,417 @@
+#include <minmax.h>
+#include <net.h>
+#include "pxe.h"
+#include "url.h"
+#include "tftp.h"
+
+const uint8_t TimeoutTable[] = {
+ 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
+ 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
+};
+struct tftp_packet {
+ uint16_t opcode;
+ uint16_t serial;
+ char data[];
+};
+
+static void tftp_error(struct inode *file, uint16_t errnum,
+ const char *errstr);
+
+static void tftp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ if (!socket->tftp_goteof) {
+ tftp_error(inode, 0, "No error, file close");
+ }
+ net_core_close(socket);
+}
+
+/**
+ * Send an ERROR packet. This is used to terminate a connection.
+ *
+ * @inode: Inode structure
+ * @errnum: Error number (network byte order)
+ * @errstr: Error string (included in packet)
+ */
+static void tftp_error(struct inode *inode, uint16_t errnum,
+ const char *errstr)
+{
+ static struct {
+ uint16_t err_op;
+ uint16_t err_num;
+ char err_msg[64];
+ } __packed err_buf;
+ int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ err_buf.err_op = TFTP_ERROR;
+ err_buf.err_num = errnum;
+ memcpy(err_buf.err_msg, errstr, len);
+ err_buf.err_msg[len] = '\0';
+
+ net_core_send(socket, &err_buf, 4 + len + 1);
+}
+
+/**
+ * Send ACK packet. This is a common operation and so is worth canning.
+ *
+ * @param: inode, Inode pointer
+ * @param: ack_num, Packet # to ack (host byte order)
+ *
+ */
+static void ack_packet(struct inode *inode, uint16_t ack_num)
+{
+ static uint16_t ack_packet_buf[2];
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ /* Packet number to ack */
+ ack_packet_buf[0] = TFTP_ACK;
+ ack_packet_buf[1] = htons(ack_num);
+
+ net_core_send(socket, ack_packet_buf, 4);
+}
+
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void tftp_get_packet(struct inode *inode)
+{
+ uint16_t last_pkt;
+ const uint8_t *timeout_ptr;
+ uint8_t timeout;
+ uint16_t buffersize;
+ uint16_t serial;
+ jiffies_t oldtime;
+ struct tftp_packet *pkt = NULL;
+ uint16_t buf_len;
+ struct pxe_pvt_inode *socket = PVT(inode);
+ uint16_t src_port;
+ uint32_t src_ip;
+ int err;
+
+ /*
+ * Start by ACKing the previous packet; this should cause
+ * the next packet to be sent.
+ */
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ oldtime = jiffies();
+
+ ack_again:
+ ack_packet(inode, socket->tftp_lastpkt);
+
+ while (timeout) {
+ buf_len = socket->tftp_blksize + 4;
+ err = net_core_recv(socket, socket->tftp_pktbuf, &buf_len,
+ &src_ip, &src_port);
+ if (err) {
+ jiffies_t now = jiffies();
+
+ if (now-oldtime >= timeout) {
+ oldtime = now;
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ break;
+ goto ack_again;
+ }
+ continue;
+ }
+
+ if (buf_len < 4) /* Bad size for a DATA packet */
+ continue;
+
+ pkt = (struct tftp_packet *)(socket->tftp_pktbuf);
+ if (pkt->opcode != TFTP_DATA) /* Not a data packet */
+ continue;
+
+ /* If goes here, recevie OK, break */
+ break;
+ }
+
+ /* time runs out */
+ if (timeout == 0)
+ kaboom();
+
+ last_pkt = socket->tftp_lastpkt;
+ last_pkt++;
+ serial = ntohs(pkt->serial);
+ if (serial != last_pkt) {
+ /*
+ * Wrong packet, ACK the packet and try again.
+ * This is presumably because the ACK got lost,
+ * so the server just resent the previous packet.
+ */
+#if 0
+ printf("Wrong packet, wanted %04x, got %04x\n", \
+ htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
+ goto ack_again;
+ }
+
+ /* It's the packet we want. We're also EOF if the size < blocksize */
+ socket->tftp_lastpkt = last_pkt; /* Update last packet number */
+ buffersize = buf_len - 4; /* Skip TFTP header */
+ socket->tftp_dataptr = socket->tftp_pktbuf + 4;
+ socket->tftp_filepos += buffersize;
+ socket->tftp_bytesleft = buffersize;
+ if (buffersize < socket->tftp_blksize) {
+ /* it's the last block, ACK packet immediately */
+ ack_packet(inode, serial);
+
+ /* Make sure we know we are at end of file */
+ inode->size = socket->tftp_filepos;
+ socket->tftp_goteof = 1;
+ tftp_close_file(inode);
+ }
+}
+
+const struct pxe_conn_ops tftp_conn_ops = {
+ .fill_buffer = tftp_get_packet,
+ .close = tftp_close_file,
+};
+
+/**
+ * Open a TFTP connection to the server
+ *
+ * @param:inode, the inode to store our state in
+ * @param:ip, the ip to contact to get the file
+ * @param:filename, the file we wanna open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+void tftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ char *buf;
+ uint16_t buf_len;
+ char *p;
+ char *options;
+ char *data;
+ static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
+ char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
+ char reply_packet_buf[PKTBUF_SIZE];
+ int err;
+ int buffersize;
+ int rrq_len;
+ const uint8_t *timeout_ptr;
+ jiffies_t timeout;
+ jiffies_t oldtime;
+ uint16_t opcode;
+ uint16_t blk_num;
+ uint64_t opdata;
+ uint16_t src_port;
+ uint32_t src_ip;
+
+ (void)redir; /* TFTP does not redirect */
+ (void)flags;
+
+ if (url->type != URL_OLD_TFTP) {
+ /*
+ * The TFTP URL specification allows the TFTP to end with a
+ * ;mode= which we just ignore.
+ */
+ url_unescape(url->path, ';');
+ }
+
+ if (!url->port)
+ url->port = TFTP_PORT;
+
+ socket->ops = &tftp_conn_ops;
+ if (net_core_open(socket, NET_CORE_UDP))
+ return;
+
+ buf = rrq_packet_buf;
+ *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
+ buf += 2;
+
+ buf = stpcpy(buf, url->path);
+
+ buf++; /* Point *past* the final NULL */
+ memcpy(buf, rrq_tail, sizeof rrq_tail);
+ buf += sizeof rrq_tail;
+
+ rrq_len = buf - rrq_packet_buf;
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+sendreq:
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ return; /* No file available... */
+ oldtime = jiffies();
+
+ /* net_core_open() calls netconn_bind() */
+ net_core_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port);
+
+ /* If the WRITE call fails, we let the timeout take care of it... */
+wait_pkt:
+ for (;;) {
+ buf_len = sizeof(reply_packet_buf);
+
+ err = net_core_recv(socket, reply_packet_buf, &buf_len,
+ &src_ip, &src_port);
+ if (err) {
+ jiffies_t now = jiffies();
+ if (now - oldtime >= timeout)
+ goto sendreq;
+ } else {
+ /* Make sure the packet actually came from the server and
+ is long enough for a TFTP opcode */
+ dprintf("tftp_open: got packet buflen=%d\n", buf_len);
+ if ((src_ip == url->ip) && (buf_len >= 2))
+ break;
+ }
+ }
+
+ net_core_disconnect(socket);
+ net_core_connect(socket, src_ip, src_port);
+
+ /* filesize <- -1 == unknown */
+ inode->size = -1;
+ socket->tftp_blksize = TFTP_BLOCKSIZE;
+ buffersize = buf_len - 2; /* bytes after opcode */
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)reply_packet_buf;
+ switch (opcode) {
+ case TFTP_ERROR:
+ inode->size = 0;
+ goto done; /* ERROR reply; don't try again */
+
+ case TFTP_DATA:
+ /*
+ * If the server doesn't support any options, we'll get a
+ * DATA reply instead of OACK. Stash the data in the file
+ * buffer and go with the default value for all options...
+ *
+ * We got a DATA packet, meaning no options are
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
+ */
+ buffersize -= 2;
+ if (buffersize < 0)
+ goto wait_pkt;
+ data = reply_packet_buf + 2;
+ blk_num = ntohs(*(uint16_t *)data);
+ data += 2;
+ if (blk_num != 1)
+ goto wait_pkt;
+ socket->tftp_lastpkt = blk_num;
+ if (buffersize > TFTP_BLOCKSIZE)
+ goto err_reply; /* Corrupt */
+
+ socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4);
+ if (!socket->tftp_pktbuf)
+ goto err_reply; /* Internal error */
+
+ if (buffersize < TFTP_BLOCKSIZE) {
+ /*
+ * This is the final EOF packet, already...
+ * We know the filesize, but we also want to
+ * ack the packet and set the EOF flag.
+ */
+ inode->size = buffersize;
+ socket->tftp_goteof = 1;
+ ack_packet(inode, blk_num);
+ }
+
+ socket->tftp_bytesleft = buffersize;
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ memcpy(socket->tftp_pktbuf, data, buffersize);
+ goto done;
+
+ case TFTP_OACK:
+ /*
+ * Now we need to parse the OACK packet to get the transfer
+ * and packet sizes.
+ */
+
+ options = reply_packet_buf + 2;
+ p = options;
+
+ while (buffersize) {
+ const char *opt = p;
+
+ /*
+ * If we find an option which starts with a NUL byte,
+ * (a null option), we're either seeing garbage that some
+ * TFTP servers add to the end of the packet, or we have
+ * no clue how to parse the rest of the packet (what is
+ * an option name and what is a value?) In either case,
+ * discard the rest.
+ */
+ if (!*opt)
+ goto done;
+
+ while (buffersize) {
+ if (!*p)
+ break; /* Found a final null */
+ *p++ |= 0x20;
+ buffersize--;
+ }
+ if (!buffersize)
+ break; /* Unterminated option */
+
+ /* Consume the terminal null */
+ p++;
+ buffersize--;
+
+ if (!buffersize)
+ break; /* No option data */
+
+ opdata = 0;
+
+ /* do convert a number-string to decimal number, just like atoi */
+ while (buffersize--) {
+ uint8_t d = *p++;
+ if (d == '\0')
+ break; /* found a final null */
+ d -= '0';
+ if (d > 9)
+ goto err_reply; /* Not a decimal digit */
+ opdata = opdata*10 + d;
+ }
+
+ if (!strcmp(opt, "tsize"))
+ inode->size = opdata;
+ else if (!strcmp(opt, "blksize"))
+ socket->tftp_blksize = opdata;
+ else
+ goto err_reply; /* Non-negotitated option returned,
+ no idea what it means ...*/
+
+
+ }
+
+ if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE)
+ goto err_reply;
+
+ /* Parsing successful, allocate buffer */
+ socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4);
+ if (!socket->tftp_pktbuf)
+ goto err_reply;
+ else
+ goto done;
+
+ default:
+ printf("TFTP unknown opcode %d\n", ntohs(opcode));
+ goto err_reply;
+ }
+
+err_reply:
+ /* Build the TFTP error packet */
+ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
+ inode->size = 0;
+
+done:
+ if (!inode->size)
+ net_core_close(socket);
+
+ return;
+}
diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h
new file mode 100644
index 00000000..114c221f
--- /dev/null
+++ b/core/fs/pxe/tftp.h
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tftp.h
+ */
+#ifndef PXE_TFTP_H
+#define PXE_TFTP_H
+
+/*
+ * TFTP default port number
+ */
+#define TFTP_PORT 69
+
+/*
+ * TFTP default block size
+ */
+#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ htons(1) // Read rest
+#define TFTP_WRQ htons(2) // Write rest
+#define TFTP_DATA htons(3) // Data packet
+#define TFTP_ACK htons(4) // ACK packet
+#define TFTP_ERROR htons(5) // ERROR packet
+#define TFTP_OACK htons(6) // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF htons(0) // Unspecified error
+#define TFTP_ENOTFOUND htons(1) // File not found
+#define TFTP_EACCESS htons(2) // Access violation
+#define TFTP_ENOSPACE htons(3) // Disk full
+#define TFTP_EBADOP htons(4) // Invalid TFTP operation
+#define TFTP_EBADID htons(5) // Unknown transfer
+#define TFTP_EEXISTS htons(6) // File exists
+#define TFTP_ENOUSER htons(7) // No such user
+#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+
+#endif /* PXE_TFTP_H */
diff --git a/core/fs/pxe/url.h b/core/fs/pxe/url.h
new file mode 100644
index 00000000..53984f3a
--- /dev/null
+++ b/core/fs/pxe/url.h
@@ -0,0 +1,33 @@
+/*
+ * url.h
+ */
+
+#ifndef CORE_PXE_URL_H
+#define CORE_PXE_URL_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+enum url_type {
+ URL_NORMAL, /* It is a full URL */
+ URL_OLD_TFTP, /* It's a ::-style TFTP path */
+ URL_SUFFIX /* Prepend the pathname prefix */
+};
+
+struct url_info {
+ char *scheme;
+ char *user;
+ char *passwd;
+ char *host;
+ uint32_t ip; /* Placeholder field not set by parse_url() */
+ unsigned int port;
+ char *path; /* Includes query */
+ enum url_type type;
+};
+
+enum url_type url_type(const char *url);
+void parse_url(struct url_info *ui, char *url);
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize);
+char *url_unescape(char *buffer, char terminator);
+
+#endif /* CORE_PXE_URL_H */
diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c
new file mode 100644
index 00000000..6b73ddb6
--- /dev/null
+++ b/core/fs/pxe/urlparse.c
@@ -0,0 +1,223 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * urlparse.c
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "url.h"
+
+/*
+ * Return the type of a URL without modifying the string
+ */
+enum url_type url_type(const char *url)
+{
+ const char *q;
+
+ q = strchr(url, ':');
+ if (!q)
+ return URL_SUFFIX;
+
+ if (q[1] == '/' && q[2] == '/')
+ return URL_NORMAL;
+
+ if (q[1] == ':')
+ return URL_OLD_TFTP;
+
+ return URL_SUFFIX;
+}
+
+/*
+ * Decompose a URL into its components. This is done in-place;
+ * this routine does not allocate any additional storage. Freeing the
+ * original buffer frees all storage used.
+ */
+void parse_url(struct url_info *ui, char *url)
+{
+ char *p = url;
+ char *q, *r, *s;
+ int c;
+
+ memset(ui, 0, sizeof *ui);
+
+ q = strchr(p, ':');
+ if (q && (q[1] == '/' && q[2] == '/')) {
+ ui->type = URL_NORMAL;
+
+ ui->scheme = p;
+ *q = '\0';
+ p = q+3;
+
+ q = strchr(p, '/');
+ if (q) {
+ *q = '\0';
+ ui->path = q+1;
+ q = strchr(q+1, '#');
+ if (q)
+ *q = '\0';
+ } else {
+ ui->path = "";
+ }
+
+ r = strchr(p, '@');
+ if (r) {
+ ui->user = p;
+ *r = '\0';
+ s = strchr(p, ':');
+ if (s) {
+ *s = '\0';
+ ui->passwd = s+1;
+ }
+ p = r+1;
+ }
+
+ ui->host = p;
+ r = strchr(p, ':');
+ if (r) {
+ *r++ = '\0';
+ ui->port = 0;
+ while ((c = *r++)) {
+ c -= '0';
+ if (c > 9)
+ break;
+ ui->port = ui->port * 10 + c;
+ }
+ }
+ } else if (q && q[1] == ':') {
+ *q = '\0';
+ ui->scheme = "tftp";
+ ui->host = p;
+ ui->path = q+2;
+ ui->type = URL_OLD_TFTP;
+ } else {
+ ui->path = p;
+ ui->type = URL_SUFFIX;
+ }
+}
+
+/*
+ * Escapes unsafe characters in a URL.
+ * This does *not* escape things like query characters!
+ * Returns the number of characters in the total output.
+ */
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize)
+{
+ static const char uchexchar[] = "0123456789ABCDEF";
+ const char *p;
+ unsigned char c;
+ char *q;
+ size_t n = 0;
+
+ q = output;
+ for (p = input; (c = *p); p++) {
+ if (c <= ' ' || c > '~') {
+ if (++n < bufsize) *q++ = '%';
+ if (++n < bufsize) *q++ = uchexchar[c >> 4];
+ if (++n < bufsize) *q++ = uchexchar[c & 15];
+ } else {
+ if (++n < bufsize) *q++ = c;
+ }
+ }
+
+ *q = '\0';
+ return n;
+}
+
+static int hexdigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ c |= 0x20;
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+/*
+ * Unescapes a buffer, optionally ending at an *unescaped* terminator
+ * (like ; for TFTP). The unescaping is done in-place.
+ *
+ * If a terminator is reached, return a pointer to the first character
+ * after the terminator.
+ */
+char *url_unescape(char *buffer, char terminator)
+{
+ char *p = buffer;
+ char *q = buffer;
+ unsigned char c;
+ int x, y;
+
+ while ((c = *p)) {
+ if (c == terminator) {
+ *q = '\0';
+ return p;
+ }
+ p++;
+ if (c == '%') {
+ x = hexdigit(p[0]);
+ if (x >= 0) {
+ y = hexdigit(p[1]);
+ if (y >= 0) {
+ *q++ = (x << 4) + y;
+ p += 2;
+ continue;
+ }
+ }
+ }
+ *q++ = c;
+ }
+ *q = '\0';
+ return NULL;
+}
+
+#ifdef URL_TEST
+
+int main(int argc, char *argv[])
+{
+ int i;
+ struct url_info url;
+
+ for (i = 1; i < argc; i++) {
+ parse_url(&url, argv[i]);
+ printf("scheme: %s\n"
+ "user: %s\n"
+ "passwd: %s\n"
+ "host: %s\n"
+ "port: %d\n"
+ "path: %s\n"
+ "type: %d\n",
+ url.scheme, url.user, url.passwd, url.host, url.port,
+ url.path, url.type);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/core/fs/readdir.c b/core/fs/readdir.c
index d071affd..546a704a 100644
--- a/core/fs/readdir.c
+++ b/core/fs/readdir.c
@@ -1,3 +1,4 @@
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/dirent.h>
@@ -7,12 +8,12 @@
/*
* Open a directory
*/
-DIR *opendir(const char *path)
+__export DIR *opendir(const char *path)
{
int rv;
struct file *file;
- rv = searchdir(path);
+ rv = searchdir(path, O_RDONLY|O_DIRECTORY);
if (rv < 0)
return NULL;
@@ -29,7 +30,7 @@ DIR *opendir(const char *path)
/*
* Read one directory entry at one time.
*/
-struct dirent *readdir(DIR *dir)
+__export struct dirent *readdir(DIR *dir)
{
static struct dirent buf;
struct file *dd_dir = (struct file *)dir;
@@ -47,7 +48,7 @@ struct dirent *readdir(DIR *dir)
/*
* Close a directory
*/
-int closedir(DIR *dir)
+__export int closedir(DIR *dir)
{
struct file *dd_dir = (struct file *)dir;
_close_file(dd_dir);
diff --git a/core/fs/xfs/misc.h b/core/fs/xfs/misc.h
new file mode 100644
index 00000000..7f2f1b33
--- /dev/null
+++ b/core/fs/xfs/misc.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MISC_H_
+#define MISC_H_
+
+/* Return a 64-bit litte-endian value from a given 64-bit big-endian one */
+static inline uint64_t be64_to_cpu(uint64_t val)
+{
+ return (uint64_t)((((uint64_t)val & (uint64_t)0x00000000000000ffULL) << 56) |
+ (((uint64_t)val & (uint64_t)0x000000000000ff00ULL) << 40) |
+ (((uint64_t)val & (uint64_t)0x0000000000ff0000ULL) << 24) |
+ (((uint64_t)val & (uint64_t)0x00000000ff000000ULL) << 8) |
+ (((uint64_t)val & (uint64_t)0x000000ff00000000ULL) >> 8) |
+ (((uint64_t)val & (uint64_t)0x0000ff0000000000ULL) >> 24) |
+ (((uint64_t)val & (uint64_t)0x00ff000000000000ULL) >> 40) |
+ (((uint64_t)val & (uint64_t)0xff00000000000000ULL) >> 56));
+}
+
+/* Return a 32-bit litte-endian value from a given 32-bit big-endian one */
+static inline uint32_t be32_to_cpu(uint32_t val)
+{
+ return (uint32_t)((((uint32_t)val & (uint32_t)0x000000ffUL) << 24) |
+ (((uint32_t)val & (uint32_t)0x0000ff00UL) << 8) |
+ (((uint32_t)val & (uint32_t)0x00ff0000UL) >> 8) |
+ (((uint32_t)val & (uint32_t)0xff000000UL) >> 24));
+}
+
+/* Return a 16-bit litte-endian value from a given 16-bit big-endian one */
+static inline uint16_t be16_to_cpu(uint16_t val)
+{
+ return (uint16_t)((((uint16_t)val & (uint16_t)0x00ffU) << 8) |
+ (((uint16_t)val & (uint16_t)0xff00U) >> 8));
+}
+
+#endif /* MISC_H_ */
diff --git a/core/fs/xfs/xfs.c b/core/fs/xfs/xfs.c
new file mode 100644
index 00000000..b6a396aa
--- /dev/null
+++ b/core/fs/xfs/xfs.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <cache.h>
+#include <core.h>
+#include <disk.h>
+#include <fs.h>
+#include <ilog2.h>
+#include <klibc/compiler.h>
+#include <ctype.h>
+
+#include "codepage.h"
+#include "xfs_types.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "misc.h"
+#include "xfs.h"
+#include "xfs_dinode.h"
+#include "xfs_dir2.h"
+#include "xfs_readdir.h"
+
+static inline int xfs_fmt_local_readdir(struct file *file,
+ struct dirent *dirent,
+ xfs_dinode_t *core)
+{
+ return xfs_readdir_dir2_local(file, dirent, core);
+}
+
+static inline int xfs_fmt_extents_readdir(struct file *file,
+ struct dirent *dirent,
+ xfs_dinode_t *core)
+{
+ if (be32_to_cpu(core->di_nextents) <= 1) {
+ /* Single-block Directories */
+ return xfs_readdir_dir2_block(file, dirent, core);
+ } else if (xfs_dir2_isleaf(file->fs, core)) {
+ /* Leaf Directory */
+ return xfs_readdir_dir2_leaf(file, dirent, core);
+ } else {
+ /* Node Directory */
+ return xfs_readdir_dir2_node(file, dirent, core);
+ }
+}
+
+static int xfs_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ xfs_dinode_t *core;
+ struct inode *inode = file->inode;
+
+ xfs_debug("file %p dirent %p");
+
+ core = xfs_dinode_get_core(fs, inode->ino);
+ if (!core) {
+ xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino);
+ return -1;
+ }
+
+ if (core->di_format == XFS_DINODE_FMT_LOCAL)
+ return xfs_fmt_local_readdir(file, dirent, core);
+ else if (core->di_format == XFS_DINODE_FMT_EXTENTS)
+ return xfs_fmt_extents_readdir(file, dirent, core);
+
+ return -1;
+}
+
+static uint32_t xfs_getfssec(struct file *file, char *buf, int sectors,
+ bool *have_more)
+{
+ return generic_getfssec(file, buf, sectors, have_more);
+}
+
+static int xfs_next_extent(struct inode *inode, uint32_t lstart)
+{
+ struct fs_info *fs = inode->fs;
+ xfs_dinode_t *core = NULL;
+ xfs_bmbt_irec_t rec;
+ block_t bno;
+ xfs_bmdr_block_t *rblock;
+ int fsize;
+ xfs_bmbt_ptr_t *pp;
+ xfs_btree_block_t *blk;
+ uint16_t nextents;
+ block_t nextbno;
+ uint32_t index;
+
+ (void)lstart;
+
+ xfs_debug("inode %p lstart %lu", inode, lstart);
+
+ core = xfs_dinode_get_core(fs, inode->ino);
+ if (!core) {
+ xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino);
+ goto out;
+ }
+
+ /* The data fork contains the file's data extents */
+ if (XFS_PVT(inode)->i_cur_extent == be32_to_cpu(core->di_nextents))
+ goto out;
+
+ if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+ bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] +
+ XFS_PVT(inode)->i_cur_extent++);
+
+ bno = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs);
+
+ XFS_PVT(inode)->i_offset = rec.br_startoff;
+
+ inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) >> SECTOR_SHIFT(fs);
+ inode->next_extent.len = ((rec.br_blockcount << BLOCK_SHIFT(fs)) +
+ SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs);
+ } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
+ xfs_debug("XFS_DINODE_FMT_BTREE");
+ index = XFS_PVT(inode)->i_cur_extent++;
+ rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0];
+ fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK);
+ pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0));
+ bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs);
+
+ /* Find the leaf */
+ for (;;) {
+ blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+ if (be16_to_cpu(blk->bb_level) == 0)
+ break;
+
+ pp = XFS_BMBT_PTR_ADDR(fs, blk, 1,
+ xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0));
+ bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs);
+ }
+
+ /* Find the right extent among threaded leaves */
+ for (;;) {
+ nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib);
+ nextents = be16_to_cpu(blk->bb_numrecs);
+ if (nextents - index > 0) {
+ bmbt_irec_get(&rec, XFS_BMDR_REC_ADDR(blk, index + 1));
+
+ bno = fsblock_to_bytes(fs, rec.br_startblock)
+ >> BLOCK_SHIFT(fs);
+
+ XFS_PVT(inode)->i_offset = rec.br_startoff;
+
+ inode->next_extent.pstart = bno << BLOCK_SHIFT(fs)
+ >> SECTOR_SHIFT(fs);
+ inode->next_extent.len = ((rec.br_blockcount
+ << BLOCK_SHIFT(fs))
+ + SECTOR_SIZE(fs) - 1)
+ >> SECTOR_SHIFT(fs);
+ break;
+ }
+
+ index -= nextents;
+ bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs);
+ blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+ }
+ }
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static inline struct inode *xfs_fmt_local_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ return xfs_dir2_local_find_entry(dname, parent, core);
+}
+
+static inline struct inode *xfs_fmt_extents_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ if (be32_to_cpu(core->di_nextents) <= 1) {
+ /* Single-block Directories */
+ return xfs_dir2_block_find_entry(dname, parent, core);
+ } else if (xfs_dir2_isleaf(parent->fs, core)) {
+ /* Leaf Directory */
+ return xfs_dir2_leaf_find_entry(dname, parent, core);
+ } else {
+ /* Node Directory */
+ return xfs_dir2_node_find_entry(dname, parent, core);
+ }
+}
+
+static inline struct inode *xfs_fmt_btree_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ return xfs_dir2_node_find_entry(dname, parent, core);
+}
+
+static struct inode *xfs_iget(const char *dname, struct inode *parent)
+{
+ struct fs_info *fs = parent->fs;
+ xfs_dinode_t *core = NULL;
+ struct inode *inode = NULL;
+
+ xfs_debug("dname %s parent %p parent ino %lu", dname, parent, parent->ino);
+
+ core = xfs_dinode_get_core(fs, parent->ino);
+ if (!core) {
+ xfs_error("Failed to get dinode from disk (ino 0x%llx)", parent->ino);
+ goto out;
+ }
+
+ if (core->di_format == XFS_DINODE_FMT_LOCAL) {
+ inode = xfs_fmt_local_find_entry(dname, parent, core);
+ } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+ inode = xfs_fmt_extents_find_entry(dname, parent, core);
+ } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
+ inode = xfs_fmt_btree_find_entry(dname, parent, core);
+ }
+
+ if (!inode) {
+ xfs_debug("Entry not found!");
+ goto out;
+ }
+
+ if (inode->mode == DT_REG) {
+ XFS_PVT(inode)->i_offset = 0;
+ XFS_PVT(inode)->i_cur_extent = 0;
+ } else if (inode->mode == DT_DIR) {
+ XFS_PVT(inode)->i_btree_offset = 0;
+ XFS_PVT(inode)->i_leaf_ent_offset = 0;
+ }
+
+ return inode;
+
+out:
+ return NULL;
+}
+
+static int xfs_readlink(struct inode *inode, char *buf)
+{
+ struct fs_info *fs = inode->fs;
+ xfs_dinode_t *core;
+ int pathlen = -1;
+ xfs_bmbt_irec_t rec;
+ block_t db;
+ const char *dir_buf;
+
+ xfs_debug("inode %p buf %p", inode, buf);
+
+ core = xfs_dinode_get_core(fs, inode->ino);
+ if (!core) {
+ xfs_error("Failed to get dinode from disk (ino 0x%llx)", inode->ino);
+ goto out;
+ }
+
+ pathlen = be64_to_cpu(core->di_size);
+ if (!pathlen)
+ goto out;
+
+ if (pathlen < 0 || pathlen > MAXPATHLEN) {
+ xfs_error("inode (%llu) bad symlink length (%d)",
+ inode->ino, pathlen);
+ goto out;
+ }
+
+ if (core->di_format == XFS_DINODE_FMT_LOCAL) {
+ memcpy(buf, (char *)&core->di_literal_area[0], pathlen);
+ } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+ bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+ db = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs);
+ dir_buf = xfs_dir2_dirblks_get_cached(fs, db, rec.br_blockcount);
+
+ /*
+ * Syslinux only supports filesystem block size larger than or equal to
+ * 4 KiB. Thus, one directory block is far enough to hold the maximum
+ * symbolic link file content, which is only 1024 bytes long.
+ */
+ memcpy(buf, dir_buf, pathlen);
+ }
+
+out:
+ return pathlen;
+}
+
+static struct inode *xfs_iget_root(struct fs_info *fs)
+{
+ xfs_dinode_t *core = NULL;
+ struct inode *inode = xfs_new_inode(fs);
+
+ xfs_debug("Looking for the root inode...");
+
+ core = xfs_dinode_get_core(fs, XFS_INFO(fs)->rootino);
+ if (!core) {
+ xfs_error("Inode core's magic number does not match!");
+ xfs_debug("magic number 0x%04x", be16_to_cpu(core->di_magic));
+ goto out;
+ }
+
+ fill_xfs_inode_pvt(fs, inode, XFS_INFO(fs)->rootino);
+
+ xfs_debug("Root inode has been found!");
+
+ if ((be16_to_cpu(core->di_mode) & S_IFMT) != S_IFDIR) {
+ xfs_error("root inode is not a directory ?! No makes sense...");
+ goto out;
+ }
+
+ inode->ino = XFS_INFO(fs)->rootino;
+ inode->mode = DT_DIR;
+ inode->size = be64_to_cpu(core->di_size);
+
+ return inode;
+
+out:
+ free(inode);
+
+ return NULL;
+}
+
+static inline int xfs_read_superblock(struct fs_info *fs, xfs_sb_t *sb)
+{
+ struct disk *disk = fs->fs_dev->disk;
+
+ if (!disk->rdwr_sectors(disk, sb, XFS_SB_DADDR, 1, false))
+ return -1;
+
+ return 0;
+}
+
+static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb)
+{
+ struct xfs_fs_info *info;
+
+ info = malloc(sizeof *info);
+ if (!info)
+ malloc_error("xfs_fs_info structure");
+
+ info->blocksize = be32_to_cpu(sb->sb_blocksize);
+ info->block_shift = sb->sb_blocklog;
+ info->dirblksize = 1 << (sb->sb_blocklog + sb->sb_dirblklog);
+ info->dirblklog = sb->sb_dirblklog;
+ info->inopb_shift = sb->sb_inopblog;
+ info->agblk_shift = sb->sb_agblklog;
+ info->rootino = be64_to_cpu(sb->sb_rootino);
+ info->agblocks = be32_to_cpu(sb->sb_agblocks);
+ info->agblocks_shift = sb->sb_agblklog;
+ info->agcount = be32_to_cpu(sb->sb_agcount);
+ info->inodesize = be16_to_cpu(sb->sb_inodesize);
+ info->inode_shift = sb->sb_inodelog;
+
+ return info;
+}
+
+static int xfs_fs_init(struct fs_info *fs)
+{
+ struct disk *disk = fs->fs_dev->disk;
+ xfs_sb_t sb;
+ struct xfs_fs_info *info;
+
+ xfs_debug("fs %p", fs);
+
+ SECTOR_SHIFT(fs) = disk->sector_shift;
+ SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
+
+ if (xfs_read_superblock(fs, &sb)) {
+ xfs_error("Superblock read failed");
+ goto out;
+ }
+
+ if (!xfs_is_valid_magicnum(&sb)) {
+ xfs_error("Invalid superblock");
+ goto out;
+ }
+
+ xfs_debug("magicnum 0x%lX", be32_to_cpu(sb.sb_magicnum));
+
+ info = xfs_new_sb_info(&sb);
+ if (!info) {
+ xfs_error("Failed to fill in filesystem-specific info structure");
+ goto out;
+ }
+
+ fs->fs_info = info;
+
+ xfs_debug("block_shift %u blocksize 0x%lX (%lu)", info->block_shift,
+ info->blocksize, info->blocksize);
+
+ xfs_debug("rootino 0x%llX (%llu)", info->rootino, info->rootino);
+
+ BLOCK_SHIFT(fs) = info->block_shift;
+ BLOCK_SIZE(fs) = info->blocksize;
+
+ cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
+
+ XFS_INFO(fs)->dirleafblk = xfs_dir2_db_to_da(fs, XFS_DIR2_LEAF_FIRSTDB(fs));
+
+ return BLOCK_SHIFT(fs);
+
+out:
+ return -1;
+}
+
+const struct fs_ops xfs_fs_ops = {
+ .fs_name = "xfs",
+ .fs_flags = FS_USEMEM | FS_THISIND,
+ .fs_init = xfs_fs_init,
+ .iget_root = xfs_iget_root,
+ .searchdir = NULL,
+ .getfssec = xfs_getfssec,
+ .open_config = generic_open_config,
+ .close_file = generic_close_file,
+ .mangle_name = generic_mangle_name,
+ .readdir = xfs_readdir,
+ .iget = xfs_iget,
+ .next_extent = xfs_next_extent,
+ .readlink = xfs_readlink,
+};
diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h
new file mode 100644
index 00000000..0d953d89
--- /dev/null
+++ b/core/fs/xfs/xfs.h
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Some parts borrowed from Linux kernel tree (linux/fs/xfs):
+ *
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_H_
+#define XFS_H_
+
+#include <disk.h>
+#include <fs.h>
+#include <dprintf.h>
+
+#include "xfs_types.h"
+#include "xfs_ag.h"
+
+#define xfs_error(fmt, args...) \
+ ({ \
+ printf("%s:%u: xfs - [ERROR] " fmt "\n", __func__, __LINE__, ## args); \
+ })
+
+#define xfs_debug(fmt, args...) \
+ ({ \
+ dprintf("%s:%u: xfs - [DEBUG] " fmt "\n", __func__, __LINE__, \
+ ## args); \
+ })
+
+struct xfs_fs_info;
+
+#define XFS_INFO(fs) ((struct xfs_fs_info *)((fs)->fs_info))
+#define XFS_PVT(ino) ((struct xfs_inode *)((ino)->pvt))
+
+#define XFS_INO_MASK(k) (uint32_t)((1ULL << (k)) - 1)
+#define XFS_INO_OFFSET_BITS(fs) (fs)->inopb_shift
+#define XFS_INO_AGINO_BITS(fs) \
+ (XFS_INFO((fs))->inopb_shift + XFS_INFO((fs))->agblk_shift)
+
+#define XFS_INO_TO_AGINO(fs, i) \
+ ((xfs_agino_t)(i) & XFS_INO_MASK(XFS_INO_AGINO_BITS(fs)))
+
+#define XFS_INO_TO_AGNO(fs, ino) \
+ ((xfs_agnumber_t)((ino) >> (XFS_INFO((fs))->inopb_shift + \
+ XFS_INFO((fs))->agblk_shift)))
+
+#define XFS_INO_TO_OFFSET(fs, i) \
+ ((int)(i) & XFS_INO_MASK(XFS_INO_OFFSET_BITS(fs)))
+
+#define XFS_AGNO_TO_FSB(fs, agno) \
+ ((block_t)((agno) << XFS_INFO((fs))->agblocks_shift))
+
+#define XFS_AGI_OFFS(fs, mp) \
+ ((xfs_agi_t *)((uint8_t *)(mp) + 2 * SECTOR_SIZE((fs))))
+
+#define XFS_GET_DIR_INO4(di) \
+ (((uint32_t)(di).i[0] << 24) | ((di).i[1] << 16) | ((di).i[2] << 8) | \
+ ((di).i[3]))
+
+#define XFS_DI_HI(di) \
+ (((uint32_t)(di).i[1] << 16) | ((di).i[2] << 8) | ((di).i[3]))
+
+#define XFS_DI_LO(di) \
+ (((uint32_t)(di).i[4] << 24) | ((di).i[5] << 16) | ((di).i[6] << 8) | \
+ ((di).i[7]))
+
+#define XFS_GET_DIR_INO8(di) \
+ (((xfs_ino_t)XFS_DI_LO(di) & 0xffffffffULL) | \
+ ((xfs_ino_t)XFS_DI_HI(di) << 32))
+
+#define XFS_FSB_TO_AGNO(fs, fsbno) \
+ ((xfs_agnumber_t)((fsbno) >> XFS_INFO((fs))->agblk_shift))
+#define XFS_FSB_TO_AGBNO(fs, fsbno) \
+ ((xfs_agblock_t)((fsbno) & (uint32_t)((1ULL << \
+ XFS_INFO((fs))->agblk_shift) - 1)))
+
+#define agblock_to_bytes(fs, x) \
+ ((uint64_t)(x) << BLOCK_SHIFT((fs)))
+#define agino_to_bytes(fs, x) \
+ ((uint64_t)(x) << XFS_INFO((fs))->inode_shift)
+#define agnumber_to_bytes(fs, x) \
+ agblock_to_bytes(fs, (uint64_t)(x) * XFS_INFO((fs))->agblocks)
+#define fsblock_to_bytes(fs,x) \
+ (agnumber_to_bytes(fs, XFS_FSB_TO_AGNO(fs, (x))) + \
+ agblock_to_bytes(fs, XFS_FSB_TO_AGBNO(fs, (x))))
+#define ino_to_bytes(fs, x) \
+ (agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, (x))) + \
+ agino_to_bytes(fs, XFS_INO_TO_AGINO(fs, (x))))
+
+/* Superblock's LBA */
+#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in filesystem/ag */
+
+/* Magic numbers */
+#define XFS_AGI_MAGIC "XAGI"
+#define XFS_IBT_MAGIC "IABT"
+#define XFS_DINODE_MAGIC "IN"
+
+#define XFS_DIR2_BLOCK_MAGIC 0x58443242U /* XD2B: single block dirs */
+#define XFS_DIR2_DATA_MAGIC 0x58443244U /* XD2D: multiblock dirs */
+#define XFS_DIR2_FREE_MAGIC 0x58443246U /* XD2F: free index blocks */
+
+#define XFS_DIR2_NULL_DATAPTR ((uint32_t)0)
+
+/* File types and modes */
+#define S_IFMT 00170000
+#define S_IFSOCK 0140000
+#define S_IFLNK 0120000
+#define S_IFREG 0100000
+#define S_IFBLK 0060000
+#define S_IFDIR 0040000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#define MAXPATHLEN 1024
+/*
+ * NOTE: The fields in the superblock are stored in big-endian format on disk.
+ */
+typedef struct xfs_sb {
+ uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */
+ uint32_t sb_blocksize; /* logical block size, bytes */
+ xfs_drfsbno_t sb_dblocks; /* number of data blocks */
+ xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */
+ xfs_drtbno_t sb_rextents; /* number of realtime extents */
+ uuid_t sb_uuid; /* file system unique id */
+ xfs_dfsbno_t sb_logstart; /* starting block of log if internal */
+ xfs_ino_t sb_rootino; /* root inode number */
+ xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */
+ xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */
+ xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */
+ xfs_agblock_t sb_agblocks; /* size of an allocation group */
+ xfs_agnumber_t sb_agcount; /* number of allocation groups */
+ xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */
+ xfs_extlen_t sb_logblocks; /* number of log blocks */
+ uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */
+ uint16_t sb_sectsize; /* volume sector size, bytes */
+ uint16_t sb_inodesize; /* inode size, bytes */
+ uint16_t sb_inopblock; /* inodes per block */
+ char sb_fname[12]; /* file system name */
+ uint8_t sb_blocklog; /* log2 of sb_blocksize */
+ uint8_t sb_sectlog; /* log2 of sb_sectsize */
+ uint8_t sb_inodelog; /* log2 of sb_inodesize */
+ uint8_t sb_inopblog; /* log2 of sb_inopblock */
+ uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */
+ uint8_t sb_rextslog; /* log2 of sb_rextents */
+ uint8_t sb_inprogress; /* mkfs is in progress, don't mount */
+ uint8_t sb_imax_pct; /* max % of fs for inode space */
+ /* statistics */
+ /*
+ * These fields must remain contiguous. If you really
+ * want to change their layout, make sure you fix the
+ * code in xfs_trans_apply_sb_deltas().
+ */
+ uint64_t sb_icount; /* allocated inodes */
+ uint64_t sb_ifree; /* free inodes */
+ uint64_t sb_fdblocks; /* free data blocks */
+ uint64_t sb_frextents; /* free realtime extents */
+ /*
+ * End contiguous fields.
+ */
+ xfs_ino_t sb_uquotino; /* user quota inode */
+ xfs_ino_t sb_gquotino; /* group quota inode */
+ uint16_t sb_qflags; /* quota flags */
+ uint8_t sb_flags; /* misc. flags */
+ uint8_t sb_shared_vn; /* shared version number */
+ xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */
+ uint32_t sb_unit; /* stripe or raid unit */
+ uint32_t sb_width; /* stripe or raid width */
+ uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */
+ uint8_t sb_logsectlog; /* log2 of the log sector size */
+ uint16_t sb_logsectsize; /* sector size for the log, bytes */
+ uint32_t sb_logsunit; /* stripe unit size for the log */
+ uint32_t sb_features2; /* additional feature bits */
+
+ /*
+ * bad features2 field as a result of failing to pad the sb
+ * structure to 64 bits. Some machines will be using this field
+ * for features2 bits. Easiest just to mark it bad and not use
+ * it for anything else.
+ */
+ uint32_t sb_bad_features2;
+ uint8_t pad[304]; /* must be padded to a sector boundary */
+} __attribute__((__packed__)) xfs_sb_t;
+
+/* In-memory structure that stores filesystem-specific information.
+ * The information stored is basically retrieved from the XFS superblock
+ * to be used statically around the driver.
+ */
+struct xfs_fs_info {
+ uint32_t blocksize; /* Filesystem block size */
+ uint8_t block_shift; /* Filesystem block size in bits */
+ uint32_t dirblksize;
+ uint8_t dirblklog;
+ uint8_t inopb_shift;
+ uint8_t agblk_shift;
+ uint32_t dirleafblk;
+
+ /* AG number bits (MSB of the inode number) */
+ uint8_t ag_number_ino_shift;
+
+ xfs_ino_t rootino; /* Root inode number for the filesystem */
+ xfs_agblock_t agblocks; /* Size of each AG in blocks */
+ uint8_t agblocks_shift; /* agblocks in bits */
+ xfs_agnumber_t agcount; /* Number of AGs in the filesytem */
+ uint16_t inodesize; /* Size of the inode in bytes */
+ uint8_t inode_shift; /* Inode size in bits */
+} __attribute__((__packed__));
+
+typedef struct xfs_agi {
+ /*
+ * Common allocation group header information
+ */
+ uint32_t agi_magicnum; /* magic number == XFS_AGI_MAGIC */
+ uint32_t agi_versionnum; /* header version == XFS_AGI_VERSION */
+ uint32_t agi_seqno; /* sequence # starting from 0 */
+ uint32_t agi_length; /* size in blocks of a.g. */
+ /*
+ * Inode information
+ * Inodes are mapped by interpreting the inode number, so no
+ * mapping data is needed here.
+ */
+ uint32_t agi_count; /* count of allocated inodes */
+ uint32_t agi_root; /* root of inode btree */
+ uint32_t agi_level; /* levels in inode btree */
+ uint32_t agi_freecount; /* number of free inodes */
+ uint32_t agi_newino; /* new inode just allocated */
+ uint32_t agi_dirino; /* last directory inode chunk */
+ /*
+ * Hash table of inodes which have been unlinked but are
+ * still being referenced.
+ */
+ uint32_t agi_unlinked[XFS_AGI_UNLINKED_BUCKETS];
+} __attribute__((__packed__)) xfs_agi_t;
+
+/*
+ * Bmap btree record and extent descriptor.
+ * l0:63 is an extent flag (value 1 indicates non-normal).
+ * l0:9-62 are startoff.
+ * l0:0-8 and l1:21-63 are startblock.
+ * l1:0-20 are blockcount.
+ */
+typedef struct xfs_bmbt_rec {
+ uint64_t l0;
+ uint64_t l1;
+} __attribute__((__packed__)) xfs_bmbt_rec_t;
+
+typedef xfs_bmbt_rec_t xfs_bmdr_rec_t;
+
+/*
+ * Possible extent states.
+ */
+typedef enum {
+ XFS_EXT_NORM,
+ XFS_EXT_UNWRITTEN,
+ XFS_EXT_DMAPI_OFFLINE,
+ XFS_EXT_INVALID,
+} xfs_exntst_t;
+
+typedef struct xfs_bmbt_irec
+{
+ xfs_fileoff_t br_startoff; /* starting file offset */
+ xfs_fsblock_t br_startblock; /* starting block number */
+ xfs_filblks_t br_blockcount; /* number of blocks */
+ xfs_exntst_t br_state; /* extent state */
+} __attribute__((__packed__)) xfs_bmbt_irec_t;
+
+static inline void bmbt_irec_get(xfs_bmbt_irec_t *dest,
+ const xfs_bmbt_rec_t *src)
+{
+ uint64_t l0, l1;
+
+ l0 = be64_to_cpu(src->l0);
+ l1 = be64_to_cpu(src->l1);
+
+ dest->br_startoff = ((xfs_fileoff_t)l0 & 0x7ffffffffffffe00ULL) >> 9;
+ dest->br_startblock = (((xfs_fsblock_t)l0 & 0x00000000000001ffULL) << 43) |
+ (((xfs_fsblock_t)l1) >> 21);
+ dest->br_blockcount = (xfs_filblks_t)(l1 & 0x00000000001fffffULL);
+ dest->br_state = (l0 & 0x8000000000000000ULL) ?
+ XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+}
+
+typedef struct xfs_timestamp {
+ int32_t t_sec;
+ int32_t t_nsec;
+} __attribute__((__packed__)) xfs_timestamp_t;
+
+/*
+ * Fork identifiers.
+ */
+#define XFS_DATA_FORK 0
+#define xFS_ATTR_FORK 1
+
+typedef enum xfs_dinode_fmt {
+ XFS_DINODE_FMT_DEV,
+ XFS_DINODE_FMT_LOCAL,
+ XFS_DINODE_FMT_EXTENTS,
+ XFS_DINODE_FMT_BTREE,
+ XFS_DINODE_FMT_UUID,
+} xfs_dinode_fmt_t;
+
+typedef struct xfs_dinode {
+ uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */
+ uint16_t di_mode; /* mode and type of file */
+ uint8_t di_version; /* inode version */
+ uint8_t di_format; /* format of di_c data */
+ uint16_t di_onlink; /* old number of links to file */
+ uint32_t di_uid; /* owner's user id */
+ uint32_t di_gid; /* owner's group id */
+ uint32_t di_nlink; /* number of links to file */
+ uint16_t di_projid_lo; /* lower part of owner's project id */
+ uint16_t di_projid_hi; /* higher part owner's project id */
+ uint8_t di_pad[6]; /* unused, zeroed space */
+ uint16_t di_flushiter; /* incremented on flush */
+ xfs_timestamp_t di_atime; /* time last accessed */
+ xfs_timestamp_t di_mtime; /* time last modified */
+ xfs_timestamp_t di_ctime; /* time created/inode modified */
+ uint64_t di_size; /* number of bytes in file */
+ uint64_t di_nblocks; /* # of direct & btree blocks used */
+ uint32_t di_extsize; /* basic/minimum extent size for file */
+ uint32_t di_nextents; /* number of extents in data fork */
+ uint16_t di_anextents; /* number of extents in attribute fork*/
+ uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */
+ int8_t di_aformat; /* format of attr fork's data */
+ uint32_t di_dmevmask; /* DMIG event mask */
+ uint16_t di_dmstate; /* DMIG state info */
+ uint16_t di_flags; /* random flags, XFS_DIFLAG_... */
+ uint32_t di_gen; /* generation number */
+
+ /* di_next_unlinked is the only non-core field in the old dinode */
+ uint32_t di_next_unlinked;/* agi unlinked list ptr */
+ uint8_t di_literal_area[1];
+} __attribute__((packed)) xfs_dinode_t;
+
+/*
+ * Inode size for given fs.
+ */
+#define XFS_LITINO(fs) \
+ ((int)((XFS_INFO(fs)->inodesize) - sizeof(struct xfs_dinode) - 1))
+
+#define XFS_BROOT_SIZE_ADJ \
+ (XFS_BTREE_LBLOCK_LEN - sizeof(xfs_bmdr_block_t))
+
+/*
+ * Inode data & attribute fork sizes, per inode.
+ */
+#define XFS_DFORK_Q(dip) ((dip)->di_forkoff != 0)
+#define XFS_DFORK_BOFF(dip) ((int)((dip)->di_forkoff << 3))
+
+#define XFS_DFORK_DSIZE(dip, fs) \
+ (XFS_DFORK_Q(dip) ? \
+ XFS_DFORK_BOFF(dip) : \
+ XFS_LITINO(fs))
+#define XFS_DFORK_ASIZE(dip, fs) \
+ (XFS_DFORK_Q(dip) ? \
+ XFS_LITINO(fs) - XFS_DFORK_BOFF(dip) : \
+ 0)
+#define XFS_DFORK_SIZE(dip, fs, w) \
+ ((w) == XFS_DATA_FORK ? \
+ XFS_DFORK_DSIZE(dip, fs) : \
+ XFS_DFORK_ASIZE(dip, fs))
+
+struct xfs_inode {
+ xfs_agblock_t i_agblock;
+ block_t i_ino_blk;
+ uint64_t i_block_offset;
+ uint64_t i_offset;
+ uint32_t i_cur_extent;
+ uint32_t i_btree_offset;
+ uint16_t i_leaf_ent_offset;
+};
+
+typedef struct { uint8_t i[8]; } __attribute__((__packed__)) xfs_dir2_ino8_t;
+typedef struct { uint8_t i[4]; } __attribute__((__packed__)) xfs_dir2_ino4_t;
+
+typedef union {
+ xfs_dir2_ino8_t i8;
+ xfs_dir2_ino4_t i4;
+} __attribute__((__packed__)) xfs_dir2_inou_t;
+
+typedef struct { uint8_t i[2]; } __attribute__((__packed__)) xfs_dir2_sf_off_t;
+
+typedef struct xfs_dir2_sf_hdr {
+ uint8_t count; /* count of entries */
+ uint8_t i8count; /* count of 8-byte inode #s */
+ xfs_dir2_inou_t parent; /* parent dir inode number */
+} __attribute__((__packed__)) xfs_dir2_sf_hdr_t;
+
+typedef struct xfs_dir2_sf_entry {
+ uint8_t namelen; /* actual name length */
+ xfs_dir2_sf_off_t offset; /* saved offset */
+ uint8_t name[1]; /* name, variable size */
+ xfs_dir2_inou_t inumber; /* inode number, var. offset */
+} __attribute__((__packed__)) xfs_dir2_sf_entry_t;
+
+typedef struct xfs_dir2_sf {
+ xfs_dir2_sf_hdr_t hdr; /* shortform header */
+ xfs_dir2_sf_entry_t list[1]; /* shortform entries */
+} __attribute__((__packed__)) xfs_dir2_sf_t;
+
+typedef xfs_ino_t xfs_intino_t;
+
+static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp,
+ xfs_dir2_inou_t *from)
+{
+ return ((sfp)->hdr.i8count == 0 ? \
+ (xfs_intino_t)XFS_GET_DIR_INO4((from)->i4) : \
+ (xfs_intino_t)XFS_GET_DIR_INO8((from)->i8));
+}
+
+/*
+ * DIR2 Data block structures.
+ *
+ * A pure data block looks like the following drawing on disk:
+ *
+ * +-------------------------------------------------+
+ * | xfs_dir2_data_hdr_t |
+ * +-------------------------------------------------+
+ * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t |
+ * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t |
+ * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t |
+ * | ... |
+ * +-------------------------------------------------+
+ * | unused space |
+ * +-------------------------------------------------+
+ *
+ * As all the entries are variable size structure the accessors below should
+ * be used to iterate over them.
+ *
+ * In addition to the pure data blocks for the data and node formats.
+ * most structures are also used for the combined data/freespace "block"
+ * format below.
+ */
+#define XFS_DIR2_DATA_ALIGN_LOG 3
+#define XFS_DIR2_DATA_ALIGN (1 << XFS_DIR2_DATA_ALIGN_LOG)
+#define XFS_DIR2_DATA_FREE_TAG 0xffff
+#define XFS_DIR2_DATA_FD_COUNT 3
+
+/*
+ * Directory address space divided into sections.
+ * spaces separated by 32GB.
+ */
+#define XFS_DIR2_SPACE_SIZE (1ULL << (32 + XFS_DIR2_DATA_ALIGN_LOG))
+
+typedef struct xfs_dir2_data_free {
+ uint16_t offset;
+ uint16_t length;
+} __attribute__((__packed__)) xfs_dir2_data_free_t;
+
+typedef struct xfs_dir2_data_hdr {
+ uint32_t magic;
+ xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT];
+} __attribute__((__packed__)) xfs_dir2_data_hdr_t;
+
+typedef struct xfs_dir2_data_entry {
+ uint64_t inumber; /* inode number */
+ uint8_t namelen; /* name length */
+ uint8_t name[]; /* name types, no null */
+ /* uint16_t tag; */ /* starting offset of us */
+} __attribute__((__packed__)) xfs_dir2_data_entry_t;
+
+typedef struct xfs_dir2_data_unused {
+ uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */
+ uint16_t length; /* total free length */
+ /* variable offset */
+ /* uint16_t tag; */ /* starting offset of us */
+} __attribute__((__packed__)) xfs_dir2_data_unused_t;
+
+/**
+ * rol32 - rotate a 32-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint32_t rol32(uint32_t word, signed int shift)
+{
+ return (word << shift) | (word >> (32 - shift));
+}
+
+#define roundup(x, y) ( \
+{ \
+ const typeof(y) __y = y; \
+ (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+
+static inline int xfs_dir2_data_entsize(int n)
+{
+ return (int)roundup(offsetof(struct xfs_dir2_data_entry, name[0]) + n +
+ (unsigned int)sizeof(uint16_t), XFS_DIR2_DATA_ALIGN);
+}
+
+static inline uint16_t *
+xfs_dir2_data_entry_tag_p(struct xfs_dir2_data_entry *dep)
+{
+ return (uint16_t *)((char *)dep +
+ xfs_dir2_data_entsize(dep->namelen) - sizeof(uint16_t));
+}
+
+static inline uint16_t *
+xfs_dir2_data_unused_tag_p(struct xfs_dir2_data_unused *dup)
+{
+ return (uint16_t *)((char *)dup +
+ be16_to_cpu(dup->length) - sizeof(uint16_t));
+}
+
+typedef struct xfs_dir2_block_tail {
+ uint32_t count; /* count of leaf entries */
+ uint32_t stale; /* count of stale lf entries */
+} __attribute__((__packed__)) xfs_dir2_block_tail_t;
+
+static inline struct xfs_dir2_block_tail *
+xfs_dir2_block_tail_p(struct xfs_fs_info *fs_info, struct xfs_dir2_data_hdr *hdr)
+{
+ return ((struct xfs_dir2_block_tail *)
+ ((char *)hdr + fs_info->dirblksize)) - 1;
+}
+
+static inline uint32_t
+xfs_dir2_db_to_da(struct fs_info *fs, uint32_t db)
+{
+ return db << XFS_INFO(fs)->dirblklog;
+}
+
+static inline int64_t
+xfs_dir2_dataptr_to_byte(uint32_t dp)
+{
+ return (int64_t)dp << XFS_DIR2_DATA_ALIGN_LOG;
+}
+
+static inline uint32_t
+xfs_dir2_byte_to_db(struct fs_info *fs, int64_t by)
+{
+ return (uint32_t)
+ (by >> (XFS_INFO(fs)->block_shift + XFS_INFO(fs)->dirblklog));
+}
+
+static inline uint32_t
+xfs_dir2_dataptr_to_db(struct fs_info *fs, uint32_t dp)
+{
+ return xfs_dir2_byte_to_db(fs, xfs_dir2_dataptr_to_byte(dp));
+}
+
+static inline unsigned int
+xfs_dir2_byte_to_off(struct fs_info *fs, int64_t by)
+{
+ return (unsigned int)(by &
+ (( 1 << (XFS_INFO(fs)->block_shift + XFS_INFO(fs)->dirblklog)) - 1));
+}
+
+static inline unsigned int
+xfs_dir2_dataptr_to_off(struct fs_info *fs, uint32_t dp)
+{
+ return xfs_dir2_byte_to_off(fs, xfs_dir2_dataptr_to_byte(dp));
+}
+
+#define XFS_DIR2_LEAF_SPACE 1
+#define XFS_DIR2_LEAF_OFFSET (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE)
+#define XFS_DIR2_LEAF_FIRSTDB(fs) \
+ xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET)
+
+typedef struct xfs_da_blkinfo {
+ uint32_t forw;
+ uint32_t back;
+ uint16_t magic;
+ uint16_t pad;
+} __attribute__((__packed__)) xfs_da_blkinfo_t;
+
+typedef struct xfs_dir2_leaf_hdr {
+ xfs_da_blkinfo_t info;
+ uint16_t count;
+ uint16_t stale;
+} __attribute__((__packed__)) xfs_dir2_leaf_hdr_t;
+
+typedef struct xfs_dir2_leaf_entry {
+ uint32_t hashval; /* hash value of name */
+ uint32_t address; /* address of data entry */
+} __attribute__((__packed__)) xfs_dir2_leaf_entry_t;
+
+typedef struct xfs_dir2_leaf {
+ xfs_dir2_leaf_hdr_t hdr; /* leaf header */
+ xfs_dir2_leaf_entry_t ents[]; /* entries */
+} __attribute__((__packed__)) xfs_dir2_leaf_t;
+
+#define XFS_DA_NODE_MAGIC 0xfebeU /* magic number: non-leaf blocks */
+#define XFS_ATTR_LEAF_MAGIC 0xfbeeU /* magic number: attribute leaf blks */
+#define XFS_DIR2_LEAF1_MAGIC 0xd2f1U /* magic number: v2 dirlf single blks */
+#define XFS_DIR2_LEAFN_MAGIC 0xd2ffU /* magic number: V2 dirlf multi blks */
+
+typedef struct xfs_da_intnode {
+ struct xfs_da_node_hdr { /* constant-structure header block */
+ xfs_da_blkinfo_t info; /* block type, links, etc. */
+ uint16_t count; /* count of active entries */
+ uint16_t level; /* level above leaves (leaf == 0) */
+ } hdr;
+ struct xfs_da_node_entry {
+ uint32_t hashval; /* hash value for this descendant */
+ uint32_t before; /* Btree block before this key */
+ } btree[1];
+} __attribute__((__packed__)) xfs_da_intnode_t;
+
+typedef struct xfs_da_node_hdr xfs_da_node_hdr_t;
+typedef struct xfs_da_node_entry xfs_da_node_entry_t;
+
+static inline bool xfs_is_valid_magicnum(const xfs_sb_t *sb)
+{
+ return sb->sb_magicnum == *(uint32_t *)XFS_SB_MAGIC;
+}
+
+static inline bool xfs_is_valid_agi(xfs_agi_t *agi)
+{
+ return agi->agi_magicnum == *(uint32_t *)XFS_AGI_MAGIC;
+}
+
+static inline struct inode *xfs_new_inode(struct fs_info *fs)
+{
+ struct inode *inode;
+
+ inode = alloc_inode(fs, 0, sizeof(struct xfs_inode));
+ if (!inode)
+ malloc_error("xfs_inode structure");
+
+ return inode;
+}
+
+static inline void fill_xfs_inode_pvt(struct fs_info *fs, struct inode *inode,
+ xfs_ino_t ino)
+{
+ XFS_PVT(inode)->i_agblock =
+ agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, ino)) >> BLOCK_SHIFT(fs);
+ XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
+ XFS_PVT(inode)->i_block_offset = XFS_INO_TO_OFFSET(XFS_INFO(fs), ino) <<
+ XFS_INFO(fs)->inode_shift;
+}
+
+/*
+ * Generic btree header.
+ *
+ * This is a combination of the actual format used on disk for short and long
+ * format btrees. The first three fields are shared by both format, but
+ * the pointers are different and should be used with care.
+ *
+ * To get the size of the actual short or long form headers please use
+ * the size macros belows. Never use sizeof(xfs_btree_block);
+ */
+typedef struct xfs_btree_block {
+ uint32_t bb_magic; /* magic number for block type */
+ uint16_t bb_level; /* 0 is a leaf */
+ uint16_t bb_numrecs; /* current # of data records */
+ union {
+ struct {
+ uint32_t bb_leftsib;
+ uint32_t bb_rightsib;
+ } s; /* short form pointers */
+ struct {
+ uint64_t bb_leftsib;
+ uint64_t bb_rightsib;
+ } l; /* long form pointers */
+ } bb_u; /* rest */
+} xfs_btree_block_t;
+
+#define XFS_BTREE_SBLOCK_LEN 16 /* size of a short form block */
+#define XFS_BTREE_LBLOCK_LEN 24 /* size of a long form block */
+
+/*
+ * Bmap root header, on-disk form only.
+ */
+typedef struct xfs_bmdr_block {
+ uint16_t bb_level; /* 0 is a leaf */
+ uint16_t bb_numrecs; /* current # of data records */
+} xfs_bmdr_block_t;
+
+/*
+ * Key structure for non-leaf levels of the tree.
+ */
+typedef struct xfs_bmbt_key {
+ uint64_t br_startoff; /* starting file offset */
+} xfs_bmbt_key_t, xfs_bmdr_key_t;
+
+/* btree pointer type */
+typedef uint64_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t;
+
+/*
+ * Btree block header size depends on a superblock flag.
+ *
+ * (not quite yet, but soon)
+ */
+#define XFS_BMBT_BLOCK_LEN(fs) XFS_BTREE_LBLOCK_LEN
+
+#define XFS_BMBT_REC_ADDR(fs, block, index) \
+ ((xfs_bmbt_rec_t *) \
+ ((char *)(block) + \
+ XFS_BMBT_BLOCK_LEN(fs) + \
+ ((index) - 1) * sizeof(xfs_bmbt_rec_t)))
+
+#define XFS_BMBT_KEY_ADDR(fs, block, index) \
+ ((xfs_bmbt_key_t *) \
+ ((char *)(block) + \
+ XFS_BMBT_BLOCK_LEN(fs) + \
+ ((index) - 1) * sizeof(xfs_bmbt_key_t)))
+
+#define XFS_BMBT_PTR_ADDR(fs, block, index, maxrecs) \
+ ((xfs_bmbt_ptr_t *) \
+ ((char *)(block) + \
+ XFS_BMBT_BLOCK_LEN(fs) + \
+ (maxrecs) * sizeof(xfs_bmbt_key_t) + \
+ ((index) - 1) * sizeof(xfs_bmbt_ptr_t)))
+
+#define XFS_BMDR_REC_ADDR(block, index) \
+ ((xfs_bmdr_rec_t *) \
+ ((char *)(block) + \
+ sizeof(struct xfs_bmdr_block) + \
+ ((index) - 1) * sizeof(xfs_bmdr_rec_t)))
+
+#define XFS_BMDR_KEY_ADDR(block, index) \
+ ((xfs_bmdr_key_t *) \
+ ((char *)(block) + \
+ sizeof(struct xfs_bmdr_block) + \
+ ((index) - 1) * sizeof(xfs_bmdr_key_t)))
+
+#define XFS_BMDR_PTR_ADDR(block, index, maxrecs) \
+ ((xfs_bmdr_ptr_t *) \
+ ((char *)(block) + \
+ sizeof(struct xfs_bmdr_block) + \
+ (maxrecs) * sizeof(xfs_bmdr_key_t) + \
+ ((index) - 1) * sizeof(xfs_bmdr_ptr_t)))
+
+/*
+ * Calculate number of records in a bmap btree inode root.
+ */
+static inline int
+xfs_bmdr_maxrecs(int blocklen, int leaf)
+{
+ blocklen -= sizeof(xfs_bmdr_block_t);
+
+ if (leaf)
+ return blocklen / sizeof(xfs_bmdr_rec_t);
+
+ return blocklen / (sizeof(xfs_bmdr_key_t) + sizeof(xfs_bmdr_ptr_t));
+}
+
+#endif /* XFS_H_ */
diff --git a/core/fs/xfs/xfs_ag.h b/core/fs/xfs/xfs_ag.h
new file mode 100644
index 00000000..a2988b10
--- /dev/null
+++ b/core/fs/xfs/xfs_ag.h
@@ -0,0 +1,189 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_AG_H_
+#define XFS_AG_H_
+
+#include "xfs_types.h"
+
+/*
+ * Allocation group header
+ * This is divided into three structures, placed in sequential 512-byte
+ * buffers after a copy of the superblock (also in a 512-byte buffer).
+ */
+
+typedef uint32_t xfs_agino_t;
+
+struct xfs_buf;
+struct xfs_mount;
+struct xfs_trans;
+
+#define XFS_AGF_MAGIC "XAGF"
+#define XFS_AGF_VERSION 1
+#define XFS_AGI_VERSION 1
+
+#define XFS_AGF_GOOD_VERSION(v) ((v) == XFS_AGF_VERSION)
+#define XFS_AGI_GOOD_VERSION(v) ((v) == XFS_AGI_VERSION)
+
+/*
+ * Btree number 0 is bno, 1 is cnt. This value gives the size of the
+ * arrays below.
+ */
+#define XFS_BTNUM_AGF ((int)XFS_BTNUM_CNTi + 1)
+
+/*
+ * The second word of agf_levels in the first a.g. overlaps the EFS
+ * superblock's magic number. Since the magic numbers valid for EFS
+ * are > 64k, our value cannot be confused for an EFS superblock's.
+ */
+
+typedef struct xfs_agf {
+ /*
+ * Common allocation group header information
+ */
+ uint32_t agf_magicnum; /* magic number == XFS_AGF_MAGIC */
+ uint32_t agf_versionnum; /* header version == XFS_AGF_VERSION */
+ uint32_t agf_seqno; /* sequence # starting from 0 */
+ uint32_t agf_length; /* size in blocks of a.g. */
+ /*
+ * Freespace information
+ */
+ uint32_t agf_roots[XFS_BTNUM_AGF]; /* root blocks */
+ uint32_t agf_spare0; /* spare field */
+ uint32_t agf_levels[XFS_BTNUM_AGF]; /* btree levels */
+ uint32_t agf_spare1; /* spare field */
+ uint32_t agf_flfirst; /* first freelist block's index */
+ uint32_t agf_fllast; /* last freelist block's index */
+ uint32_t agf_flcount; /* count of blocks in freelist */
+ uint32_t agf_freeblks; /* total free blocks */
+ uint32_t agf_longest; /* longest free space */
+ uint32_t agf_btreeblks; /* # of blocks held in AGF btrees */
+} xfs_agf_t;
+
+#define XFS_AGF_MAGICNUM 0x00000001
+#define XFS_AGF_VERSIONNUM 0x00000002
+#define XFS_AGF_SEQNO 0x00000004
+#define XFS_AGF_LENGTH 0x00000008
+#define XFS_AGF_ROOTS 0x00000010
+#define XFS_AGF_LEVELS 0x00000020
+#define XFS_AGF_FLFIRST 0x00000040
+#define XFS_AGF_FLLAST 0x00000080
+#define XFS_AGF_FLCOUNT 0x00000100
+#define XFS_AGF_FREEBLKS 0x00000200
+#define XFS_AGF_LONGEST 0x00000400
+#define XFS_AGF_BTREEBLKS 0x00000800
+#define XFS_AGF_NUM_BITS 12
+#define XFS_AGF_ALL_BITS ((1 << XFS_AGF_NUM_BITS) - 1)
+
+#define XFS_AGF_FLAGS \
+ { XFS_AGF_MAGICNUM, "MAGICNUM" }, \
+ { XFS_AGF_VERSIONNUM, "VERSIONNUM" }, \
+ { XFS_AGF_SEQNO, "SEQNO" }, \
+ { XFS_AGF_LENGTH, "LENGTH" }, \
+ { XFS_AGF_ROOTS, "ROOTS" }, \
+ { XFS_AGF_LEVELS, "LEVELS" }, \
+ { XFS_AGF_FLFIRST, "FLFIRST" }, \
+ { XFS_AGF_FLLAST, "FLLAST" }, \
+ { XFS_AGF_FLCOUNT, "FLCOUNT" }, \
+ { XFS_AGF_FREEBLKS, "FREEBLKS" }, \
+ { XFS_AGF_LONGEST, "LONGEST" }, \
+ { XFS_AGF_BTREEBLKS, "BTREEBLKS" }
+
+/* disk block (xfs_daddr_t) in the AG */
+#define XFS_AGF_DADDR(mp) ((xfs_daddr_t)(1 << (mp)->m_sectbb_log))
+#define XFS_AGF_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGF_DADDR(mp))
+#define XFS_BUF_TO_AGF(bp) ((xfs_agf_t *)((bp)->b_addr))
+
+extern int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
+
+/*
+ * Size of the unlinked inode hash table in the agi.
+ */
+#define XFS_AGI_UNLINKED_BUCKETS 64
+
+#define XFS_AGI_MAGICNUM 0x00000001
+#define XFS_AGI_VERSIONNUM 0x00000002
+#define XFS_AGI_SEQNO 0x00000004
+#define XFS_AGI_LENGTH 0x00000008
+#define XFS_AGI_COUNT 0x00000010
+#define XFS_AGI_ROOT 0x00000020
+#define XFS_AGI_LEVEL 0x00000040
+#define XFS_AGI_FREECOUNT 0x00000080
+#define XFS_AGI_NEWINO 0x00000100
+#define XFS_AGI_DIRINO 0x00000200
+#define XFS_AGI_UNLINKED 0x00000400
+#define XFS_AGI_NUM_BITS 11
+#define XFS_AGI_ALL_BITS ((1 << XFS_AGI_NUM_BITS) - 1)
+
+/* disk block (xfs_daddr_t) in the AG */
+#define XFS_AGI_DADDR(mp) ((xfs_daddr_t)(2 << (mp)->m_sectbb_log))
+#define XFS_AGI_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGI_DADDR(mp))
+#define XFS_BUF_TO_AGI(bp) ((xfs_agi_t *)((bp)->b_addr))
+
+extern int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_agnumber_t agno, struct xfs_buf **bpp);
+
+/*
+ * The third a.g. block contains the a.g. freelist, an array
+ * of block pointers to blocks owned by the allocation btree code.
+ */
+#define XFS_AGFL_DADDR(mp) ((xfs_daddr_t)(3 << (mp)->m_sectbb_log))
+#define XFS_AGFL_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGFL_DADDR(mp))
+#define XFS_AGFL_SIZE(mp) ((mp)->m_sb.sb_sectsize / sizeof(xfs_agblock_t))
+#define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)((bp)->b_addr))
+
+typedef struct xfs_agfl {
+ uint32_t agfl_bno[1]; /* actually XFS_AGFL_SIZE(mp) */
+} xfs_agfl_t;
+
+/*
+ * tags for inode radix tree
+ */
+#define XFS_ICI_NO_TAG (-1) /* special flag for an untagged lookup
+ in xfs_inode_ag_iterator */
+#define XFS_ICI_RECLAIM_TAG 0 /* inode is to be reclaimed */
+
+#define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels)
+#define XFS_MIN_FREELIST_RAW(bl,cl,mp) \
+ (MIN(bl + 1, XFS_AG_MAXLEVELS(mp)) + MIN(cl + 1, XFS_AG_MAXLEVELS(mp)))
+#define XFS_MIN_FREELIST(a,mp) \
+ (XFS_MIN_FREELIST_RAW( \
+ be32_to_cpu((a)->agf_levels[XFS_BTNUM_BNOi]), \
+ be32_to_cpu((a)->agf_levels[XFS_BTNUM_CNTi]), mp))
+#define XFS_MIN_FREELIST_PAG(pag,mp) \
+ (XFS_MIN_FREELIST_RAW( \
+ (unsigned int)(pag)->pagf_levels[XFS_BTNUM_BNOi], \
+ (unsigned int)(pag)->pagf_levels[XFS_BTNUM_CNTi], mp))
+
+/*
+ * For checking for bad ranges of xfs_daddr_t's, covering multiple
+ * allocation groups or a single xfs_daddr_t that's a superblock copy.
+ */
+#define XFS_AG_CHECK_DADDR(mp,d,len) \
+ ((len) == 1 ? \
+ ASSERT((d) == XFS_SB_DADDR || \
+ xfs_daddr_to_agbno(mp, d) != XFS_SB_DADDR) : \
+ ASSERT(xfs_daddr_to_agno(mp, d) == \
+ xfs_daddr_to_agno(mp, (d) + (len) - 1)))
+
+#endif /* XFS_AG_H_ */
diff --git a/core/fs/xfs/xfs_dinode.c b/core/fs/xfs/xfs_dinode.c
new file mode 100644
index 00000000..55be6e2d
--- /dev/null
+++ b/core/fs/xfs/xfs_dinode.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cache.h>
+#include <core.h>
+#include <fs.h>
+
+#include "xfs_types.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "misc.h"
+#include "xfs.h"
+
+#include "xfs_dinode.h"
+
+xfs_dinode_t *xfs_dinode_get_core(struct fs_info *fs, xfs_ino_t ino)
+{
+ block_t blk;
+ xfs_dinode_t *core;
+ uint64_t offset;
+
+ xfs_debug("fs %p ino %lu", fs, ino);
+
+ blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
+ offset = XFS_INO_TO_OFFSET(XFS_INFO(fs), ino) << XFS_INFO(fs)->inode_shift;
+ if (offset > BLOCK_SIZE(fs)) {
+ xfs_error("Invalid inode offset in block!");
+ xfs_debug("offset: 0x%llx", offset);
+ goto out;
+ }
+
+ xfs_debug("blk %llu block offset 0x%llx", blk, blk << BLOCK_SHIFT(fs));
+ xfs_debug("inode offset in block (in bytes) is 0x%llx", offset);
+
+ core = (xfs_dinode_t *)((uint8_t *)get_cache(fs->fs_dev, blk) + offset);
+ if (be16_to_cpu(core->di_magic) !=
+ be16_to_cpu(*(uint16_t *)XFS_DINODE_MAGIC)) {
+ xfs_error("Inode core's magic number does not match!");
+ xfs_debug("magic number 0x%04x", (be16_to_cpu(core->di_magic)));
+ goto out;
+ }
+
+ return core;
+
+out:
+ return NULL;
+}
diff --git a/core/fs/xfs/xfs_dinode.h b/core/fs/xfs/xfs_dinode.h
new file mode 100644
index 00000000..80deec78
--- /dev/null
+++ b/core/fs/xfs/xfs_dinode.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_DINODE_H_
+#define XFS_DINODE_H_
+
+xfs_dinode_t *xfs_dinode_get_core(struct fs_info *fs, xfs_ino_t ino);
+
+#endif /* XFS_DINODE_H_ */
diff --git a/core/fs/xfs/xfs_dir2.c b/core/fs/xfs/xfs_dir2.c
new file mode 100644
index 00000000..de37ef7c
--- /dev/null
+++ b/core/fs/xfs/xfs_dir2.c
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cache.h>
+#include <core.h>
+#include <fs.h>
+
+#include "xfs_types.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "misc.h"
+#include "xfs.h"
+#include "xfs_dinode.h"
+
+#include "xfs_dir2.h"
+
+#define XFS_DIR2_DIRBLKS_CACHE_SIZE 128
+
+struct xfs_dir2_dirblks_cache {
+ block_t dc_startblock;
+ xfs_filblks_t dc_blkscount;
+ void *dc_area;
+};
+
+static struct xfs_dir2_dirblks_cache dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE];
+static unsigned char dirblks_cached_count = 0;
+
+uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen)
+{
+ uint32_t hash;
+
+ /*
+ * Do four characters at a time as long as we can.
+ */
+ for (hash = 0; namelen >= 4; namelen -=4, name += 4)
+ hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
+ (name[3] << 0) ^ rol32(hash, 7 * 4);
+
+ /*
+ * Now do the rest of the characters.
+ */
+ switch (namelen) {
+ case 3:
+ return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
+ rol32(hash, 7 * 3);
+ case 2:
+ return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2);
+ case 1:
+ return (name[0] << 0) ^ rol32(hash, 7 * 1);
+ default: /* case 0: */
+ return hash;
+ }
+}
+
+static void *get_dirblks(struct fs_info *fs, block_t startblock,
+ xfs_filblks_t c)
+{
+ int count = c << XFS_INFO(fs)->dirblklog;
+ uint8_t *p;
+ uint8_t *buf;
+ off_t offset = 0;
+
+ buf = malloc(c * XFS_INFO(fs)->dirblksize);
+ if (!buf)
+ malloc_error("buffer memory");
+
+ memset(buf, 0, XFS_INFO(fs)->dirblksize);
+
+ while (count--) {
+ p = (uint8_t *)get_cache(fs->fs_dev, startblock++);
+ memcpy(buf + offset, p, BLOCK_SIZE(fs));
+ offset += BLOCK_SIZE(fs);
+ }
+
+ return buf;
+}
+
+const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
+ xfs_filblks_t c)
+{
+ unsigned char i;
+ void *buf;
+
+ xfs_debug("fs %p startblock %llu (0x%llx) blkscount %lu", fs, startblock,
+ startblock, c);
+
+ if (!dirblks_cached_count) {
+ buf = get_dirblks(fs, startblock, c);
+
+ dirblks_cache[dirblks_cached_count].dc_startblock = startblock;
+ dirblks_cache[dirblks_cached_count].dc_blkscount = c;
+ dirblks_cache[dirblks_cached_count].dc_area = buf;
+
+ return dirblks_cache[dirblks_cached_count++].dc_area;
+ } else if (dirblks_cached_count == XFS_DIR2_DIRBLKS_CACHE_SIZE) {
+ for (i = 0; i < XFS_DIR2_DIRBLKS_CACHE_SIZE / 2; i++) {
+ unsigned char k = XFS_DIR2_DIRBLKS_CACHE_SIZE - (i + 1);
+
+ free(dirblks_cache[i].dc_area);
+ dirblks_cache[i] = dirblks_cache[k];
+ memset(&dirblks_cache[k], 0, sizeof(dirblks_cache[k]));
+ }
+
+ buf = get_dirblks(fs, startblock, c);
+
+ dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_startblock =
+ startblock;
+ dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_blkscount = c;
+ dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_area = buf;
+
+ dirblks_cached_count = XFS_DIR2_DIRBLKS_CACHE_SIZE / 2;
+
+ return dirblks_cache[dirblks_cached_count++].dc_area;
+ } else {
+ block_t block;
+ xfs_filblks_t count;
+
+ block = dirblks_cache[dirblks_cached_count - 1].dc_startblock;
+ count = dirblks_cache[dirblks_cached_count - 1].dc_blkscount;
+
+ if (block == startblock && count == c) {
+ return dirblks_cache[dirblks_cached_count - 1].dc_area;
+ } else {
+ for (i = 0; i < dirblks_cached_count; i++) {
+ block = dirblks_cache[i].dc_startblock;
+ count = dirblks_cache[i].dc_blkscount;
+
+ if (block == startblock && count == c)
+ return dirblks_cache[i].dc_area;
+ }
+
+ buf = get_dirblks(fs, startblock, c);
+
+ dirblks_cache[dirblks_cached_count].dc_startblock = startblock;
+ dirblks_cache[dirblks_cached_count].dc_blkscount = c;
+ dirblks_cache[dirblks_cached_count].dc_area = buf;
+
+ return dirblks_cache[dirblks_cached_count++].dc_area;
+ }
+ }
+
+ return NULL;
+}
+
+void xfs_dir2_dirblks_flush_cache(void)
+{
+ unsigned char i;
+
+ for (i = 0; i < dirblks_cached_count; i++) {
+ free(dirblks_cache[i].dc_area);
+ memset(&dirblks_cache[i], 0, sizeof(dirblks_cache[i]));
+ }
+
+ dirblks_cached_count = 0;
+}
+
+struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core)
+{
+ xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
+ xfs_dir2_sf_entry_t *sf_entry;
+ uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
+ struct fs_info *fs = parent->fs;
+ struct inode *inode;
+ xfs_intino_t ino;
+ xfs_dinode_t *ncore = NULL;
+
+ xfs_debug("dname %s parent %p core %p", dname, parent, core);
+ xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count);
+
+ sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
+ (!sf->hdr.i8count ? 4 : 0));
+ while (count--) {
+ uint8_t *start_name = &sf_entry->name[0];
+ uint8_t *end_name = start_name + sf_entry->namelen;
+
+ if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
+ xfs_debug("Found entry %s", dname);
+ goto found;
+ }
+
+ sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf_entry +
+ offsetof(struct xfs_dir2_sf_entry,
+ name[0]) +
+ sf_entry->namelen +
+ (sf->hdr.i8count ? 8 : 4));
+ }
+
+ return NULL;
+
+found:
+ inode = xfs_new_inode(fs);
+
+ ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
+ (uint8_t *)sf_entry +
+ offsetof(struct xfs_dir2_sf_entry,
+ name[0]) +
+ sf_entry->namelen));
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ ncore = xfs_dinode_get_core(fs, ino);
+ if (!ncore) {
+ xfs_error("Failed to get dinode!");
+ goto out;
+ }
+
+ fill_xfs_inode_pvt(fs, inode, ino);
+
+ inode->ino = ino;
+ inode->size = be64_to_cpu(ncore->di_size);
+
+ if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
+ inode->mode = DT_DIR;
+ xfs_debug("Found a directory inode!");
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
+ inode->mode = DT_REG;
+ xfs_debug("Found a file inode!");
+ xfs_debug("inode size %llu", inode->size);
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
+ inode->mode = DT_LNK;
+ xfs_debug("Found a symbolic link inode!");
+ }
+
+ return inode;
+
+out:
+ free(inode);
+
+ return NULL;
+}
+
+struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core)
+{
+ xfs_bmbt_irec_t r;
+ block_t dir_blk;
+ struct fs_info *fs = parent->fs;
+ const uint8_t *dirblk_buf;
+ uint8_t *p, *endp;
+ xfs_dir2_data_hdr_t *hdr;
+ struct inode *inode = NULL;
+ xfs_dir2_block_tail_t *btp;
+ xfs_dir2_data_unused_t *dup;
+ xfs_dir2_data_entry_t *dep;
+ xfs_intino_t ino;
+ xfs_dinode_t *ncore;
+
+ xfs_debug("dname %s parent %p core %p", dname, parent, core);
+
+ bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+ dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
+
+ dirblk_buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, r.br_blockcount);
+ hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
+ if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
+ xfs_error("Block directory header's magic number does not match!");
+ xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
+ goto out;
+ }
+
+ p = (uint8_t *)(hdr + 1);
+
+ btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
+ endp = (uint8_t *)((xfs_dir2_leaf_entry_t *)btp - be32_to_cpu(btp->count));
+
+ while (p < endp) {
+ uint8_t *start_name;
+ uint8_t *end_name;
+
+ dup = (xfs_dir2_data_unused_t *)p;
+ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+ p += be16_to_cpu(dup->length);
+ continue;
+ }
+
+ dep = (xfs_dir2_data_entry_t *)p;
+
+ start_name = &dep->name[0];
+ end_name = start_name + dep->namelen;
+
+ if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
+ xfs_debug("Found entry %s", dname);
+ goto found;
+ }
+
+ p += xfs_dir2_data_entsize(dep->namelen);
+ }
+
+out:
+ return NULL;
+
+found:
+ inode = xfs_new_inode(fs);
+
+ ino = be64_to_cpu(dep->inumber);
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ ncore = xfs_dinode_get_core(fs, ino);
+ if (!ncore) {
+ xfs_error("Failed to get dinode!");
+ goto failed;
+ }
+
+ fill_xfs_inode_pvt(fs, inode, ino);
+
+ inode->ino = ino;
+ XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
+ inode->size = be64_to_cpu(ncore->di_size);
+
+ if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
+ inode->mode = DT_DIR;
+ xfs_debug("Found a directory inode!");
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
+ inode->mode = DT_REG;
+ xfs_debug("Found a file inode!");
+ xfs_debug("inode size %llu", inode->size);
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
+ inode->mode = DT_LNK;
+ xfs_debug("Found a symbolic link inode!");
+ }
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ return inode;
+
+failed:
+ free(inode);
+
+ return NULL;
+}
+
+struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core)
+{
+ xfs_dir2_leaf_t *leaf;
+ xfs_bmbt_irec_t irec;
+ block_t leaf_blk, dir_blk;
+ xfs_dir2_leaf_entry_t *lep;
+ int low;
+ int high;
+ int mid = 0;
+ uint32_t hash = 0;
+ uint32_t hashwant;
+ uint32_t newdb, curdb = -1;
+ xfs_dir2_data_entry_t *dep;
+ struct inode *ip;
+ xfs_dir2_data_hdr_t *data_hdr;
+ uint8_t *start_name;
+ uint8_t *end_name;
+ xfs_intino_t ino;
+ xfs_dinode_t *ncore;
+ const uint8_t *buf = NULL;
+
+ xfs_debug("dname %s parent %p core %p", dname, parent, core);
+
+ bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
+ be32_to_cpu(core->di_nextents) - 1);
+ leaf_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
+ BLOCK_SHIFT(parent->fs);
+
+ leaf = (xfs_dir2_leaf_t *)xfs_dir2_dirblks_get_cached(parent->fs, leaf_blk,
+ irec.br_blockcount);
+ if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
+ xfs_error("Single leaf block header's magic number does not match!");
+ goto out;
+ }
+
+ if (!leaf->hdr.count)
+ goto out;
+
+ hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
+
+ /* Binary search */
+ for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
+ low <= high; ) {
+ mid = (low + high) >> 1;
+ if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
+ break;
+ if (hash < hashwant)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ }
+
+ /* If hash is not the one we want, then the directory does not contain the
+ * entry we're looking for and there is nothing to do anymore.
+ */
+ if (hash != hashwant)
+ goto out;
+
+ while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
+ mid--;
+
+ for (lep = &leaf->ents[mid];
+ mid < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->hashval) == hashwant;
+ lep++, mid++) {
+ /* Skip over stale leaf entries. */
+ if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+
+ newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
+ if (newdb != curdb) {
+ bmbt_irec_get(&irec,
+ ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + newdb);
+ dir_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
+
+ BLOCK_SHIFT(parent->fs);
+ buf = xfs_dir2_dirblks_get_cached(parent->fs, dir_blk, irec.br_blockcount);
+ data_hdr = (xfs_dir2_data_hdr_t *)buf;
+ if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+ xfs_error("Leaf directory's data magic No. does not match!");
+ goto out;
+ }
+
+ curdb = newdb;
+ }
+
+ dep = (xfs_dir2_data_entry_t *)((char *)buf +
+ xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
+
+ start_name = &dep->name[0];
+ end_name = start_name + dep->namelen;
+
+ if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
+ xfs_debug("Found entry %s", dname);
+ goto found;
+ }
+ }
+
+out:
+ return NULL;
+
+found:
+ ip = xfs_new_inode(parent->fs);
+
+ ino = be64_to_cpu(dep->inumber);
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ ncore = xfs_dinode_get_core(parent->fs, ino);
+ if (!ncore) {
+ xfs_error("Failed to get dinode!");
+ goto failed;
+ }
+
+ fill_xfs_inode_pvt(parent->fs, ip, ino);
+
+ ip->ino = ino;
+ XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
+ BLOCK_SHIFT(parent->fs);
+ ip->size = be64_to_cpu(ncore->di_size);
+
+ if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
+ ip->mode = DT_DIR;
+ xfs_debug("Found a directory inode!");
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
+ ip->mode = DT_REG;
+ xfs_debug("Found a file inode!");
+ xfs_debug("inode size %llu", ip->size);
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
+ ip->mode = DT_LNK;
+ xfs_debug("Found a symbolic link inode!");
+ }
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ return ip;
+
+failed:
+ free(ip);
+
+ return ip;
+}
+
+static xfs_fsblock_t
+select_child(xfs_dfiloff_t off,
+ xfs_bmbt_key_t *kp,
+ xfs_bmbt_ptr_t *pp,
+ int nrecs)
+{
+ int i;
+
+ for (i = 0; i < nrecs; i++) {
+ if (be64_to_cpu(kp[i].br_startoff) == off)
+ return be64_to_cpu(pp[i]);
+ if (be64_to_cpu(kp[i].br_startoff) > off) {
+ if (i == 0)
+ return be64_to_cpu(pp[i]);
+ else
+ return be64_to_cpu(pp[i-1]);
+ }
+ }
+
+ return be64_to_cpu(pp[nrecs - 1]);
+}
+
+block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
+ block_t fsblkno, int *error)
+{
+ uint32_t idx;
+ xfs_bmbt_irec_t irec;
+ block_t bno;
+ block_t nextbno;
+ xfs_bmdr_block_t *rblock;
+ int fsize;
+ int nextents;
+ xfs_bmbt_ptr_t *pp;
+ xfs_bmbt_key_t *kp;
+ xfs_btree_block_t *blk;
+ xfs_bmbt_rec_t *xp;
+
+ *error = 0;
+ if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+ xfs_debug("XFS_DINODE_FMT_EXTENTS");
+ for (idx = 0; idx < be32_to_cpu(core->di_nextents); idx++) {
+ bmbt_irec_get(&irec,
+ ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + idx);
+ if (fsblkno >= irec.br_startoff &&
+ fsblkno < irec.br_startoff + irec.br_blockcount)
+ break;
+ }
+ } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
+ xfs_debug("XFS_DINODE_FMT_BTREE");
+ bno = NULLFSBLOCK;
+ rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0];
+ fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK);
+ pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0));
+ kp = XFS_BMDR_KEY_ADDR(rblock, 1);
+ bno = fsblock_to_bytes(fs,
+ select_child(fsblkno, kp, pp,
+ be16_to_cpu(rblock->bb_numrecs))) >> BLOCK_SHIFT(fs);
+
+ /* Find the leaf */
+ for (;;) {
+ blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+ if (be16_to_cpu(blk->bb_level) == 0)
+ break;
+ pp = XFS_BMBT_PTR_ADDR(fs, blk, 1,
+ xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0));
+ kp = XFS_BMBT_KEY_ADDR(fs, blk, 1);
+ bno = fsblock_to_bytes(fs,
+ select_child(fsblkno, kp, pp,
+ be16_to_cpu(blk->bb_numrecs))) >> BLOCK_SHIFT(fs);
+ }
+
+ /* Find the records among leaves */
+ for (;;) {
+ nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib);
+ nextents = be16_to_cpu(blk->bb_numrecs);
+ xp = (xfs_bmbt_rec_t *)XFS_BMBT_REC_ADDR(fs, blk, 1);
+ for (idx = 0; idx < nextents; idx++) {
+ bmbt_irec_get(&irec, xp + idx);
+ if (fsblkno >= irec.br_startoff &&
+ fsblkno < irec.br_startoff + irec.br_blockcount) {
+ nextbno = NULLFSBLOCK;
+ break;
+ }
+ }
+ if (nextbno == NULLFSBLOCK)
+ break;
+ bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs);
+ blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+ }
+ }
+
+ if (fsblkno < irec.br_startoff ||
+ fsblkno >= irec.br_startoff + irec.br_blockcount)
+ *error = 1;
+
+ return fsblock_to_bytes(fs,
+ fsblkno - irec.br_startoff + irec.br_startblock) >>
+ BLOCK_SHIFT(fs);
+}
+
+struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core)
+{
+ block_t fsblkno;
+ xfs_da_intnode_t *node = NULL;
+ uint32_t hashwant;
+ uint32_t hash = 0;
+ xfs_da_node_entry_t *btree;
+ uint16_t max;
+ uint16_t span;
+ uint16_t probe;
+ int error;
+ xfs_dir2_data_hdr_t *data_hdr;
+ xfs_dir2_leaf_t *leaf;
+ xfs_dir2_leaf_entry_t *lep;
+ xfs_dir2_data_entry_t *dep;
+ struct inode *ip;
+ uint8_t *start_name;
+ uint8_t *end_name;
+ int low;
+ int high;
+ int mid = 0;
+ uint32_t newdb, curdb = -1;
+ xfs_intino_t ino;
+ xfs_dinode_t *ncore;
+ const uint8_t *buf = NULL;
+
+ xfs_debug("dname %s parent %p core %p", dname, parent, core);
+
+ hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
+
+ fsblkno = xfs_dir2_get_right_blk(parent->fs, core,
+ xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET),
+ &error);
+ if (error) {
+ xfs_error("Cannot find right rec!");
+ return NULL;
+ }
+
+ node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(parent->fs, fsblkno,
+ 1);
+ if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
+ xfs_error("Node's magic number does not match!");
+ goto out;
+ }
+
+ do {
+ if (!node->hdr.count)
+ goto out;
+
+ /* Given a hash to lookup, you read the node's btree array and first
+ * "hashval" in the array that exceeds the given hash and it can then
+ * be found in the block pointed by the "before" value.
+ */
+ max = be16_to_cpu(node->hdr.count);
+
+ probe = span = max/2;
+ for (btree = &node->btree[probe];
+ span > 4; btree = &node->btree[probe]) {
+ span /= 2;
+ hash = be32_to_cpu(btree->hashval);
+
+ if (hash < hashwant)
+ probe += span;
+ else if (hash > hashwant)
+ probe -= span;
+ else
+ break;
+ }
+
+ while ((probe > 0) && (be32_to_cpu(btree->hashval) >= hashwant)) {
+ btree--;
+ probe--;
+ }
+
+ while ((probe < max) && (be32_to_cpu(btree->hashval) < hashwant)) {
+ btree++;
+ probe++;
+ }
+
+ if (probe == max)
+ fsblkno = be32_to_cpu(node->btree[max-1].before);
+ else
+ fsblkno = be32_to_cpu(node->btree[probe].before);
+
+ fsblkno = xfs_dir2_get_right_blk(parent->fs, core, fsblkno, &error);
+ if (error) {
+ xfs_error("Cannot find right rec!");
+ goto out;
+ }
+
+ node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(parent->fs,
+ fsblkno, 1);
+ } while(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC);
+
+ leaf = (xfs_dir2_leaf_t*)node;
+ if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
+ xfs_error("Leaf's magic number does not match!");
+ goto out;
+ }
+
+ if (!leaf->hdr.count)
+ goto out;
+
+ for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
+ low <= high; ) {
+ mid = (low + high) >> 1;
+
+ if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
+ break;
+ if (hash < hashwant)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ }
+
+ /* If hash is not the one we want, then the directory does not contain the
+ * entry we're looking for and there is nothing to do anymore.
+ */
+ if (hash != hashwant)
+ goto out;
+
+ while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
+ mid--;
+
+ for (lep = &leaf->ents[mid];
+ mid < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->hashval) == hashwant;
+ lep++, mid++) {
+ /* Skip over stale leaf entries. */
+ if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+
+ newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
+ if (newdb != curdb) {
+ fsblkno = xfs_dir2_get_right_blk(parent->fs, core, newdb, &error);
+ if (error) {
+ xfs_error("Cannot find data block!");
+ goto out;
+ }
+
+ buf = xfs_dir2_dirblks_get_cached(parent->fs, fsblkno, 1);
+ data_hdr = (xfs_dir2_data_hdr_t *)buf;
+ if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+ xfs_error("Leaf directory's data magic No. does not match!");
+ goto out;
+ }
+
+ curdb = newdb;
+ }
+
+ dep = (xfs_dir2_data_entry_t *)((char *)buf +
+ xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
+
+ start_name = &dep->name[0];
+ end_name = start_name + dep->namelen;
+
+ if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
+ xfs_debug("Found entry %s", dname);
+ goto found;
+ }
+ }
+
+out:
+ return NULL;
+
+found:
+ ip = xfs_new_inode(parent->fs);
+ ino = be64_to_cpu(dep->inumber);
+ ncore = xfs_dinode_get_core(parent->fs, ino);
+ if (!ncore) {
+ xfs_error("Failed to get dinode!");
+ goto failed;
+ }
+
+ fill_xfs_inode_pvt(parent->fs, ip, ino);
+ ip->ino = ino;
+ XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
+ BLOCK_SHIFT(parent->fs);
+ ip->size = be64_to_cpu(ncore->di_size);
+
+ if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
+ ip->mode = DT_DIR;
+ xfs_debug("Found a directory inode!");
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
+ ip->mode = DT_REG;
+ xfs_debug("Found a file inode!");
+ xfs_debug("inode size %llu", ip->size);
+ } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
+ ip->mode = DT_LNK;
+ xfs_debug("Found a symbolic link inode!");
+ }
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ return ip;
+
+failed:
+ free(ip);
+
+ return NULL;
+}
diff --git a/core/fs/xfs/xfs_dir2.h b/core/fs/xfs/xfs_dir2.h
new file mode 100644
index 00000000..158cf44f
--- /dev/null
+++ b/core/fs/xfs/xfs_dir2.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_DIR2_H_
+#define XFS_DIR2_H_
+
+#include <core.h>
+#include <fs.h>
+
+#include "xfs.h"
+
+const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
+ xfs_filblks_t c);
+void xfs_dir2_dirblks_flush_cache(void);
+
+uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen);
+
+block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
+ block_t fsblkno, int *error);
+
+struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core);
+struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core);
+struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core);
+struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
+ xfs_dinode_t *core);
+
+static inline bool xfs_dir2_isleaf(struct fs_info *fs, xfs_dinode_t *dip)
+{
+ uint64_t last = 0;
+ xfs_bmbt_irec_t irec;
+
+ bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&dip->di_literal_area[0]) +
+ be32_to_cpu(dip->di_nextents) - 1);
+ last = irec.br_startoff + irec.br_blockcount;
+
+ return (last == XFS_INFO(fs)->dirleafblk + (1 << XFS_INFO(fs)->dirblklog));
+}
+
+static inline int xfs_dir2_entry_name_cmp(uint8_t *start, uint8_t *end,
+ const char *name)
+{
+ if (!name || (strlen(name) != end - start))
+ return -1;
+
+ while (start < end)
+ if (*start++ != *name++)
+ return -1;
+
+ return 0;
+}
+
+#endif /* XFS_DIR2_H_ */
diff --git a/core/fs/xfs/xfs_fs.h b/core/fs/xfs/xfs_fs.h
new file mode 100644
index 00000000..587820ec
--- /dev/null
+++ b/core/fs/xfs/xfs_fs.h
@@ -0,0 +1,501 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 1995-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_FS_H_
+#define XFS_FS_H_
+
+/*
+ * SGI's XFS filesystem's major stuff (constants, structures)
+ */
+
+/*
+ * Direct I/O attribute record used with XFS_IOC_DIOINFO
+ * d_miniosz is the min xfer size, xfer size multiple and file seek offset
+ * alignment.
+ */
+struct dioattr {
+ uint32_t d_mem; /* data buffer memory alignment */
+ uint32_t d_miniosz; /* min xfer size */
+ uint32_t d_maxiosz; /* max xfer size */
+};
+
+/*
+ * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR.
+ */
+struct fsxattr {
+ uint32_t fsx_xflags; /* xflags field value (get/set) */
+ uint32_t fsx_extsize; /* extsize field value (get/set)*/
+ uint32_t fsx_nextents; /* nextents field value (get) */
+ uint32_t fsx_projid; /* project identifier (get/set) */
+ unsigned char fsx_pad[12];
+};
+
+/*
+ * Flags for the bs_xflags/fsx_xflags field
+ * There should be a one-to-one correspondence between these flags and the
+ * XFS_DIFLAG_s.
+ */
+#define XFS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */
+#define XFS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */
+#define XFS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */
+#define XFS_XFLAG_APPEND 0x00000010 /* all writes append */
+#define XFS_XFLAG_SYNC 0x00000020 /* all writes synchronous */
+#define XFS_XFLAG_NOATIME 0x00000040 /* do not update access time */
+#define XFS_XFLAG_NODUMP 0x00000080 /* do not include in backups */
+#define XFS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */
+#define XFS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */
+#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
+#define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */
+#define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */
+#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */
+#define XFS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */
+#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
+
+/*
+ * Structure for XFS_IOC_GETBMAP.
+ * On input, fill in bmv_offset and bmv_length of the first structure
+ * to indicate the area of interest in the file, and bmv_entries with
+ * the number of array elements given back. The first structure is
+ * updated on return to give the offset and length for the next call.
+ */
+struct getbmap {
+ int64_t bmv_offset; /* file offset of segment in blocks */
+ int64_t bmv_block; /* starting block (64-bit daddr_t) */
+ int64_t bmv_length; /* length of segment, blocks */
+ int32_t bmv_count; /* # of entries in array incl. 1st */
+ int32_t bmv_entries; /* # of entries filled in (output) */
+};
+
+/*
+ * Structure for XFS_IOC_GETBMAPX. Fields bmv_offset through bmv_entries
+ * are used exactly as in the getbmap structure. The getbmapx structure
+ * has additional bmv_iflags and bmv_oflags fields. The bmv_iflags field
+ * is only used for the first structure. It contains input flags
+ * specifying XFS_IOC_GETBMAPX actions. The bmv_oflags field is filled
+ * in by the XFS_IOC_GETBMAPX command for each returned structure after
+ * the first.
+ */
+struct getbmapx {
+ int64_t bmv_offset; /* file offset of segment in blocks */
+ int64_t bmv_block; /* starting block (64-bit daddr_t) */
+ int64_t bmv_length; /* length of segment, blocks */
+ int32_t bmv_count; /* # of entries in array incl. 1st */
+ int32_t bmv_entries; /* # of entries filled in (output). */
+ int32_t bmv_iflags; /* input flags (1st structure) */
+ int32_t bmv_oflags; /* output flags (after 1st structure)*/
+ int32_t bmv_unused1; /* future use */
+ int32_t bmv_unused2; /* future use */
+};
+
+/* bmv_iflags values - set by XFS_IOC_GETBMAPX caller. */
+#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */
+#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */
+#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */
+#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */
+#define BMV_IF_NO_HOLES 0x10 /* Do not return holes */
+#define BMV_IF_VALID \
+ (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC| \
+ BMV_IF_DELALLOC|BMV_IF_NO_HOLES)
+
+/* bmv_oflags values - returned for each non-header segment */
+#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
+#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */
+#define BMV_OF_LAST 0x4 /* segment is the last in the file */
+
+/*
+ * Structure for XFS_IOC_FSSETDM.
+ * For use by backup and restore programs to set the XFS on-disk inode
+ * fields di_dmevmask and di_dmstate. These must be set to exactly and
+ * only values previously obtained via xfs_bulkstat! (Specifically the
+ * xfs_bstat_t fields bs_dmevmask and bs_dmstate.)
+ */
+struct fsdmidata {
+ uint32_t fsd_dmevmask; /* corresponds to di_dmevmask */
+ __u16 fsd_padding;
+ __u16 fsd_dmstate; /* corresponds to di_dmstate */
+};
+
+/*
+ * File segment locking set data type for 64 bit access.
+ * Also used for all the RESV/FREE interfaces.
+ */
+typedef struct xfs_flock64 {
+ __s16 l_type;
+ __s16 l_whence;
+ int64_t l_start;
+ int64_t l_len; /* len == 0 means until end of file */
+ int32_t l_sysid;
+ uint32_t l_pid;
+ int32_t l_pad[4]; /* reserve area */
+} xfs_flock64_t;
+
+/*
+ * Output for XFS_IOC_FSGEOMETRY_V1
+ */
+typedef struct xfs_fsop_geom_v1 {
+ uint32_t blocksize; /* filesystem (data) block size */
+ uint32_t rtextsize; /* realtime extent size */
+ uint32_t agblocks; /* fsblocks in an AG */
+ uint32_t agcount; /* number of allocation groups */
+ uint32_t logblocks; /* fsblocks in the log */
+ uint32_t sectsize; /* (data) sector size, bytes */
+ uint32_t inodesize; /* inode size in bytes */
+ uint32_t imaxpct; /* max allowed inode space(%) */
+ uint64_t datablocks; /* fsblocks in data subvolume */
+ uint64_t rtblocks; /* fsblocks in realtime subvol */
+ uint64_t rtextents; /* rt extents in realtime subvol*/
+ uint64_t logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ uint32_t sunit; /* stripe unit, fsblocks */
+ uint32_t swidth; /* stripe width, fsblocks */
+ int32_t version; /* structure version */
+ uint32_t flags; /* superblock version flags */
+ uint32_t logsectsize; /* log sector size, bytes */
+ uint32_t rtsectsize; /* realtime sector size, bytes */
+ uint32_t dirblocksize; /* directory block size, bytes */
+} xfs_fsop_geom_v1_t;
+
+/*
+ * Output for XFS_IOC_FSGEOMETRY
+ */
+typedef struct xfs_fsop_geom {
+ uint32_t blocksize; /* filesystem (data) block size */
+ uint32_t rtextsize; /* realtime extent size */
+ uint32_t agblocks; /* fsblocks in an AG */
+ uint32_t agcount; /* number of allocation groups */
+ uint32_t logblocks; /* fsblocks in the log */
+ uint32_t sectsize; /* (data) sector size, bytes */
+ uint32_t inodesize; /* inode size in bytes */
+ uint32_t imaxpct; /* max allowed inode space(%) */
+ uint64_t datablocks; /* fsblocks in data subvolume */
+ uint64_t rtblocks; /* fsblocks in realtime subvol */
+ uint64_t rtextents; /* rt extents in realtime subvol*/
+ uint64_t logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ uint32_t sunit; /* stripe unit, fsblocks */
+ uint32_t swidth; /* stripe width, fsblocks */
+ int32_t version; /* structure version */
+ uint32_t flags; /* superblock version flags */
+ uint32_t logsectsize; /* log sector size, bytes */
+ uint32_t rtsectsize; /* realtime sector size, bytes */
+ uint32_t dirblocksize; /* directory block size, bytes */
+ uint32_t logsunit; /* log stripe unit, bytes */
+} xfs_fsop_geom_t;
+
+/* Output for XFS_FS_COUNTS */
+typedef struct xfs_fsop_counts {
+ uint64_t freedata; /* free data section blocks */
+ uint64_t freertx; /* free rt extents */
+ uint64_t freeino; /* free inodes */
+ uint64_t allocino; /* total allocated inodes */
+} xfs_fsop_counts_t;
+
+/* Input/Output for XFS_GET_RESBLKS and XFS_SET_RESBLKS */
+typedef struct xfs_fsop_resblks {
+ uint64_t resblks;
+ uint64_t resblks_avail;
+} xfs_fsop_resblks_t;
+
+#define XFS_FSOP_GEOM_VERSION 0
+
+#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */
+#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */
+#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */
+#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */
+#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */
+#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */
+#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */
+#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */
+#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */
+#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */
+#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */
+
+
+/*
+ * Minimum and maximum sizes need for growth checks
+ */
+#define XFS_MIN_AG_BLOCKS 64
+#define XFS_MIN_LOG_BLOCKS 512ULL
+#define XFS_MAX_LOG_BLOCKS (1024 * 1024ULL)
+#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024ULL)
+
+/* keep the maximum size under 2^31 by a small amount */
+#define XFS_MAX_LOG_BYTES \
+ ((2 * 1024 * 1024 * 1024ULL) - XFS_MIN_LOG_BYTES)
+
+/* Used for sanity checks on superblock */
+#define XFS_MAX_DBLOCKS(s) ((xfs_drfsbno_t)(s)->sb_agcount * (s)->sb_agblocks)
+#define XFS_MIN_DBLOCKS(s) ((xfs_drfsbno_t)((s)->sb_agcount - 1) * \
+ (s)->sb_agblocks + XFS_MIN_AG_BLOCKS)
+
+/*
+ * Structures for XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG & XFS_IOC_FSGROWFSRT
+ */
+typedef struct xfs_growfs_data {
+ uint64_t newblocks; /* new data subvol size, fsblocks */
+ uint32_t imaxpct; /* new inode space percentage limit */
+} xfs_growfs_data_t;
+
+typedef struct xfs_growfs_log {
+ uint32_t newblocks; /* new log size, fsblocks */
+ uint32_t isint; /* 1 if new log is internal */
+} xfs_growfs_log_t;
+
+typedef struct xfs_growfs_rt {
+ uint64_t newblocks; /* new realtime size, fsblocks */
+ uint32_t extsize; /* new realtime extent size, fsblocks */
+} xfs_growfs_rt_t;
+
+
+/*
+ * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE
+ */
+typedef struct xfs_bstime {
+ time_t tv_sec; /* seconds */
+ int32_t tv_nsec; /* and nanoseconds */
+} xfs_bstime_t;
+
+typedef struct xfs_bstat {
+ uint64_t bs_ino; /* inode number */
+ __u16 bs_mode; /* type and mode */
+ __u16 bs_nlink; /* number of links */
+ uint32_t bs_uid; /* user id */
+ uint32_t bs_gid; /* group id */
+ uint32_t bs_rdev; /* device value */
+ int32_t bs_blksize; /* block size */
+ int64_t bs_size; /* file size */
+ xfs_bstime_t bs_atime; /* access time */
+ xfs_bstime_t bs_mtime; /* modify time */
+ xfs_bstime_t bs_ctime; /* inode change time */
+ int64_t bs_blocks; /* number of blocks */
+ uint32_t bs_xflags; /* extended flags */
+ int32_t bs_extsize; /* extent size */
+ int32_t bs_extents; /* number of extents */
+ uint32_t bs_gen; /* generation count */
+ __u16 bs_projid_lo; /* lower part of project id */
+#define bs_projid bs_projid_lo /* (previously just bs_projid) */
+ __u16 bs_forkoff; /* inode fork offset in bytes */
+ __u16 bs_projid_hi; /* higher part of project id */
+ unsigned char bs_pad[10]; /* pad space, unused */
+ uint32_t bs_dmevmask; /* DMIG event mask */
+ __u16 bs_dmstate; /* DMIG state info */
+ __u16 bs_aextents; /* attribute number of extents */
+} xfs_bstat_t;
+
+/*
+ * The user-level BulkStat Request interface structure.
+ */
+typedef struct xfs_fsop_bulkreq {
+ uint64_t __user *lastip; /* last inode # pointer */
+ int32_t icount; /* count of entries in buffer */
+ void __user *ubuffer;/* user buffer for inode desc. */
+ int32_t __user *ocount; /* output count pointer */
+} xfs_fsop_bulkreq_t;
+
+
+/*
+ * Structures returned from xfs_inumbers routine (XFS_IOC_FSINUMBERS).
+ */
+typedef struct xfs_inogrp {
+ uint64_t xi_startino; /* starting inode number */
+ int32_t xi_alloccount; /* # bits set in allocmask */
+ uint64_t xi_allocmask; /* mask of allocated inodes */
+} xfs_inogrp_t;
+
+
+/*
+ * Error injection.
+ */
+typedef struct xfs_error_injection {
+ int32_t fd;
+ int32_t errtag;
+} xfs_error_injection_t;
+
+
+/*
+ * The user-level Handle Request interface structure.
+ */
+typedef struct xfs_fsop_handlereq {
+ uint32_t fd; /* fd for FD_TO_HANDLE */
+ void __user *path; /* user pathname */
+ uint32_t oflags; /* open flags */
+ void __user *ihandle;/* user supplied handle */
+ uint32_t ihandlen; /* user supplied length */
+ void __user *ohandle;/* user buffer for handle */
+ uint32_t __user *ohandlen;/* user buffer length */
+} xfs_fsop_handlereq_t;
+
+/*
+ * Compound structures for passing args through Handle Request interfaces
+ * xfs_fssetdm_by_handle, xfs_attrlist_by_handle, xfs_attrmulti_by_handle
+ * - ioctls: XFS_IOC_FSSETDM_BY_HANDLE, XFS_IOC_ATTRLIST_BY_HANDLE, and
+ * XFS_IOC_ATTRMULTI_BY_HANDLE
+ */
+
+typedef struct xfs_fsop_setdm_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle information */
+ struct fsdmidata __user *data; /* DMAPI data */
+} xfs_fsop_setdm_handlereq_t;
+
+typedef struct xfs_attrlist_cursor {
+ uint32_t opaque[4];
+} xfs_attrlist_cursor_t;
+
+typedef struct xfs_fsop_attrlist_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle interface structure */
+ struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */
+ uint32_t flags; /* which namespace to use */
+ uint32_t buflen; /* length of buffer supplied */
+ void __user *buffer; /* returned names */
+} xfs_fsop_attrlist_handlereq_t;
+
+typedef struct xfs_attr_multiop {
+ uint32_t am_opcode;
+#define ATTR_OP_GET 1 /* return the indicated attr's value */
+#define ATTR_OP_SET 2 /* set/create the indicated attr/value pair */
+#define ATTR_OP_REMOVE 3 /* remove the indicated attr */
+ int32_t am_error;
+ void __user *am_attrname;
+ void __user *am_attrvalue;
+ uint32_t am_length;
+ uint32_t am_flags;
+} xfs_attr_multiop_t;
+
+typedef struct xfs_fsop_attrmulti_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle interface structure */
+ uint32_t opcount;/* count of following multiop */
+ struct xfs_attr_multiop __user *ops; /* attr_multi data */
+} xfs_fsop_attrmulti_handlereq_t;
+
+/*
+ * per machine unique filesystem identifier types.
+ */
+typedef struct { uint32_t val[2]; } xfs_fsid_t; /* file system id type */
+
+typedef struct xfs_fid {
+ __u16 fid_len; /* length of remainder */
+ __u16 fid_pad;
+ uint32_t fid_gen; /* generation number */
+ uint64_t fid_ino; /* 64 bits inode number */
+} xfs_fid_t;
+
+typedef struct xfs_handle {
+ union {
+ int64_t align; /* force alignment of ha_fid */
+ xfs_fsid_t _ha_fsid; /* unique file system identifier */
+ } ha_u;
+ xfs_fid_t ha_fid; /* file system specific file ID */
+} xfs_handle_t;
+#define ha_fsid ha_u._ha_fsid
+
+#define XFS_HSIZE(handle) (((char *) &(handle).ha_fid.fid_pad \
+ - (char *) &(handle)) \
+ + (handle).ha_fid.fid_len)
+
+/*
+ * Flags for going down operation
+ */
+#define XFS_FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */
+#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */
+#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */
+
+/*
+ * ioctl commands that are used by Linux filesystems
+ */
+#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS
+#define XFS_IOC_SETXFLAGS FS_IOC_SETFLAGS
+#define XFS_IOC_GETVERSION FS_IOC_GETVERSION
+
+/*
+ * ioctl commands that replace IRIX fcntl()'s
+ * For 'documentation' purposed more than anything else,
+ * the "cmd #" field reflects the IRIX fcntl number.
+ */
+#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64)
+#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64)
+#define XFS_IOC_DIOINFO _IOR ('X', 30, struct dioattr)
+#define XFS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
+#define XFS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr)
+#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64)
+#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64)
+#define XFS_IOC_GETBMAP _IOWR('X', 38, struct getbmap)
+#define XFS_IOC_FSSETDM _IOW ('X', 39, struct fsdmidata)
+#define XFS_IOC_RESVSP _IOW ('X', 40, struct xfs_flock64)
+#define XFS_IOC_UNRESVSP _IOW ('X', 41, struct xfs_flock64)
+#define XFS_IOC_RESVSP64 _IOW ('X', 42, struct xfs_flock64)
+#define XFS_IOC_UNRESVSP64 _IOW ('X', 43, struct xfs_flock64)
+#define XFS_IOC_GETBMAPA _IOWR('X', 44, struct getbmap)
+#define XFS_IOC_FSGETXATTRA _IOR ('X', 45, struct fsxattr)
+/* XFS_IOC_SETBIOSIZE ---- deprecated 46 */
+/* XFS_IOC_GETBIOSIZE ---- deprecated 47 */
+#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap)
+#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64)
+
+/*
+ * ioctl commands that replace IRIX syssgi()'s
+ */
+#define XFS_IOC_FSGEOMETRY_V1 _IOR ('X', 100, struct xfs_fsop_geom_v1)
+#define XFS_IOC_FSBULKSTAT _IOWR('X', 101, struct xfs_fsop_bulkreq)
+#define XFS_IOC_FSBULKSTAT_SINGLE _IOWR('X', 102, struct xfs_fsop_bulkreq)
+#define XFS_IOC_FSINUMBERS _IOWR('X', 103, struct xfs_fsop_bulkreq)
+#define XFS_IOC_PATH_TO_FSHANDLE _IOWR('X', 104, struct xfs_fsop_handlereq)
+#define XFS_IOC_PATH_TO_HANDLE _IOWR('X', 105, struct xfs_fsop_handlereq)
+#define XFS_IOC_FD_TO_HANDLE _IOWR('X', 106, struct xfs_fsop_handlereq)
+#define XFS_IOC_OPEN_BY_HANDLE _IOWR('X', 107, struct xfs_fsop_handlereq)
+#define XFS_IOC_READLINK_BY_HANDLE _IOWR('X', 108, struct xfs_fsop_handlereq)
+#define XFS_IOC_SWAPEXT _IOWR('X', 109, struct xfs_swapext)
+#define XFS_IOC_FSGROWFSDATA _IOW ('X', 110, struct xfs_growfs_data)
+#define XFS_IOC_FSGROWFSLOG _IOW ('X', 111, struct xfs_growfs_log)
+#define XFS_IOC_FSGROWFSRT _IOW ('X', 112, struct xfs_growfs_rt)
+#define XFS_IOC_FSCOUNTS _IOR ('X', 113, struct xfs_fsop_counts)
+#define XFS_IOC_SET_RESBLKS _IOWR('X', 114, struct xfs_fsop_resblks)
+#define XFS_IOC_GET_RESBLKS _IOR ('X', 115, struct xfs_fsop_resblks)
+#define XFS_IOC_ERROR_INJECTION _IOW ('X', 116, struct xfs_error_injection)
+#define XFS_IOC_ERROR_CLEARALL _IOW ('X', 117, struct xfs_error_injection)
+/* XFS_IOC_ATTRCTL_BY_HANDLE -- deprecated 118 */
+/* XFS_IOC_FREEZE -- FIFREEZE 119 */
+/* XFS_IOC_THAW -- FITHAW 120 */
+#define XFS_IOC_FSSETDM_BY_HANDLE _IOW ('X', 121, struct xfs_fsop_setdm_handlereq)
+#define XFS_IOC_ATTRLIST_BY_HANDLE _IOW ('X', 122, struct xfs_fsop_attrlist_handlereq)
+#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
+#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom)
+#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t)
+/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
+
+
+#ifndef HAVE_BBMACROS
+/*
+ * Block I/O parameterization. A basic block (BB) is the lowest size of
+ * filesystem allocation, and must equal 512. Length units given to bio
+ * routines are in BB's.
+ */
+#define BBSHIFT 9
+#define BBSIZE (1<<BBSHIFT)
+#define BBMASK (BBSIZE-1)
+#define BTOBB(bytes) (((uint64_t)(bytes) + BBSIZE - 1) >> BBSHIFT)
+#define BTOBBT(bytes) ((uint64_t)(bytes) >> BBSHIFT)
+#define BBTOB(bbs) ((bbs) << BBSHIFT)
+#endif
+
+#endif /* XFS_FS_H_ */
diff --git a/core/fs/xfs/xfs_readdir.c b/core/fs/xfs/xfs_readdir.c
new file mode 100644
index 00000000..86c8a77b
--- /dev/null
+++ b/core/fs/xfs/xfs_readdir.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cache.h>
+#include <core.h>
+#include <fs.h>
+
+#include "xfs_types.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "misc.h"
+#include "xfs.h"
+#include "xfs_dinode.h"
+#include "xfs_dir2.h"
+
+#include "xfs_readdir.h"
+
+static int fill_dirent(struct fs_info *fs, struct dirent *dirent,
+ uint32_t offset, xfs_ino_t ino, char *name,
+ size_t namelen)
+{
+ xfs_dinode_t *core;
+
+ xfs_debug("fs %p, dirent %p offset %lu ino %llu name %s namelen %llu", fs,
+ dirent, offset, ino, name, namelen);
+
+ dirent->d_ino = ino;
+ dirent->d_off = offset;
+ dirent->d_reclen = offsetof(struct dirent, d_name) + namelen + 1;
+
+ core = xfs_dinode_get_core(fs, ino);
+ if (!core) {
+ xfs_error("Failed to get dinode from disk (ino 0x%llx)", ino);
+ return -1;
+ }
+
+ if (be16_to_cpu(core->di_mode) & S_IFDIR)
+ dirent->d_type = DT_DIR;
+ else if (be16_to_cpu(core->di_mode) & S_IFREG)
+ dirent->d_type = DT_REG;
+ else if (be16_to_cpu(core->di_mode) & S_IFLNK)
+ dirent->d_type = DT_LNK;
+
+ memcpy(dirent->d_name, name, namelen);
+ dirent->d_name[namelen] = '\0';
+
+ return 0;
+}
+
+int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core)
+{
+ xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
+ xfs_dir2_sf_entry_t *sf_entry;
+ uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
+ uint32_t offset = file->offset;
+ uint8_t *start_name;
+ uint8_t *end_name;
+ xfs_ino_t ino;
+ struct fs_info *fs = file->fs;
+ int retval = 0;
+
+ xfs_debug("file %p dirent %p core %p", file, dirent, core);
+ xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count);
+
+ if (file->offset + 1 > count)
+ goto out;
+
+ file->offset++;
+
+ sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
+ (!sf->hdr.i8count ? 4 : 0));
+
+ if (file->offset - 1) {
+ offset = file->offset;
+ while (--offset) {
+ sf_entry = (xfs_dir2_sf_entry_t *)(
+ (uint8_t *)sf_entry +
+ offsetof(struct xfs_dir2_sf_entry,
+ name[0]) +
+ sf_entry->namelen +
+ (sf->hdr.i8count ? 8 : 4));
+ }
+ }
+
+ start_name = &sf_entry->name[0];
+ end_name = start_name + sf_entry->namelen;
+
+ ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
+ (uint8_t *)sf_entry +
+ offsetof(struct xfs_dir2_sf_entry,
+ name[0]) +
+ sf_entry->namelen));
+
+ retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
+ end_name - start_name);
+ if (retval)
+ xfs_error("Failed to fill in dirent structure");
+
+ return retval;
+
+out:
+ xfs_dir2_dirblks_flush_cache();
+
+ return -1;
+}
+
+int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core)
+{
+ xfs_bmbt_irec_t r;
+ block_t dir_blk;
+ struct fs_info *fs = file->fs;
+ const uint8_t *dirblk_buf;
+ uint8_t *p;
+ uint32_t offset;
+ xfs_dir2_data_hdr_t *hdr;
+ xfs_dir2_block_tail_t *btp;
+ xfs_dir2_data_unused_t *dup;
+ xfs_dir2_data_entry_t *dep;
+ uint8_t *start_name;
+ uint8_t *end_name;
+ xfs_ino_t ino;
+ int retval = 0;
+
+ xfs_debug("file %p dirent %p core %p", file, dirent, core);
+
+ bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+ dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
+
+ dirblk_buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, r.br_blockcount);
+ hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
+ if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
+ xfs_error("Block directory header's magic number does not match!");
+ xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
+ goto out;
+ }
+
+ btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
+
+ if (file->offset + 1 > be32_to_cpu(btp->count))
+ goto out;
+
+ file->offset++;
+
+ p = (uint8_t *)(hdr + 1);
+
+ if (file->offset - 1) {
+ offset = file->offset;
+ while (--offset) {
+ dep = (xfs_dir2_data_entry_t *)p;
+
+ dup = (xfs_dir2_data_unused_t *)p;
+ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+ p += be16_to_cpu(dup->length);
+ continue;
+ }
+
+ p += xfs_dir2_data_entsize(dep->namelen);
+ }
+ }
+
+ dep = (xfs_dir2_data_entry_t *)p;
+
+ start_name = &dep->name[0];
+ end_name = start_name + dep->namelen;
+
+ ino = be64_to_cpu(dep->inumber);
+
+ retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
+ end_name - start_name);
+ if (retval)
+ xfs_error("Failed to fill in dirent structure");
+
+ return retval;
+
+out:
+ xfs_dir2_dirblks_flush_cache();
+
+ return -1;
+}
+
+int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core)
+{
+ xfs_bmbt_irec_t irec;
+ struct fs_info *fs = file->fs;
+ xfs_dir2_leaf_t *leaf;
+ block_t leaf_blk, dir_blk;
+ xfs_dir2_leaf_entry_t *lep;
+ uint32_t db;
+ unsigned int offset;
+ xfs_dir2_data_entry_t *dep;
+ xfs_dir2_data_hdr_t *data_hdr;
+ uint8_t *start_name;
+ uint8_t *end_name;
+ xfs_intino_t ino;
+ const uint8_t *buf = NULL;
+ int retval = 0;
+
+ xfs_debug("file %p dirent %p core %p", file, dirent, core);
+
+ bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
+ be32_to_cpu(core->di_nextents) - 1);
+ leaf_blk = fsblock_to_bytes(fs, irec.br_startblock) >>
+ BLOCK_SHIFT(file->fs);
+
+ leaf = (xfs_dir2_leaf_t *)xfs_dir2_dirblks_get_cached(fs, leaf_blk,
+ irec.br_blockcount);
+ if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
+ xfs_error("Single leaf block header's magic number does not match!");
+ goto out;
+ }
+
+ if (!leaf->hdr.count)
+ goto out;
+
+ if (file->offset + 1 > be16_to_cpu(leaf->hdr.count))
+ goto out;
+
+ lep = &leaf->ents[file->offset++];
+
+ /* Skip over stale leaf entries */
+ for ( ; be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR;
+ lep++, file->offset++);
+
+ db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address));
+
+ bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + db);
+
+ dir_blk = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs);
+
+ buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, irec.br_blockcount);
+ data_hdr = (xfs_dir2_data_hdr_t *)buf;
+ if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+ xfs_error("Leaf directory's data magic number does not match!");
+ goto out;
+ }
+
+ offset = xfs_dir2_dataptr_to_off(fs, be32_to_cpu(lep->address));
+
+ dep = (xfs_dir2_data_entry_t *)((uint8_t *)buf + offset);
+
+ start_name = &dep->name[0];
+ end_name = start_name + dep->namelen;
+
+ ino = be64_to_cpu(dep->inumber);
+
+ retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
+ end_name - start_name);
+ if (retval)
+ xfs_error("Failed to fill in dirent structure");
+
+ return retval;
+
+out:
+ xfs_dir2_dirblks_flush_cache();
+
+ return -1;
+}
+
+int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core)
+{
+ struct fs_info *fs = file->fs;
+ xfs_bmbt_irec_t irec;
+ uint32_t node_off = 0;
+ block_t fsblkno;
+ xfs_da_intnode_t *node = NULL;
+ struct inode *inode = file->inode;
+ int error;
+ xfs_dir2_data_hdr_t *data_hdr;
+ xfs_dir2_leaf_t *leaf;
+ xfs_dir2_leaf_entry_t *lep;
+ unsigned int offset;
+ xfs_dir2_data_entry_t *dep;
+ uint8_t *start_name;
+ uint8_t *end_name;
+ uint32_t db;
+ const uint8_t *buf = NULL;
+ int retval = 0;
+
+ xfs_debug("file %p dirent %p core %p", file, dirent, core);
+
+ do {
+ bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] +
+ ++node_off);
+ } while (irec.br_startoff < xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET));
+
+ fsblkno = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs);
+
+ node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
+ if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
+ xfs_error("Node's magic number does not match!");
+ goto out;
+ }
+
+try_next_btree:
+ if (!node->hdr.count ||
+ XFS_PVT(inode)->i_btree_offset >= be16_to_cpu(node->hdr.count))
+ goto out;
+
+ fsblkno = be32_to_cpu(node->btree[XFS_PVT(inode)->i_btree_offset].before);
+ fsblkno = xfs_dir2_get_right_blk(fs, core, fsblkno, &error);
+ if (error) {
+ xfs_error("Cannot find leaf rec!");
+ goto out;
+ }
+
+ leaf = (xfs_dir2_leaf_t*)xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
+ if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
+ xfs_error("Leaf's magic number does not match!");
+ goto out;
+ }
+
+ if (!leaf->hdr.count ||
+ XFS_PVT(inode)->i_leaf_ent_offset >= be16_to_cpu(leaf->hdr.count)) {
+ XFS_PVT(inode)->i_btree_offset++;
+ XFS_PVT(inode)->i_leaf_ent_offset = 0;
+ goto try_next_btree;
+ }
+
+ lep = &leaf->ents[XFS_PVT(inode)->i_leaf_ent_offset];
+
+ /* Skip over stale leaf entries */
+ for ( ; XFS_PVT(inode)->i_leaf_ent_offset < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR;
+ lep++, XFS_PVT(inode)->i_leaf_ent_offset++);
+
+ if (XFS_PVT(inode)->i_leaf_ent_offset == be16_to_cpu(leaf->hdr.count)) {
+ XFS_PVT(inode)->i_btree_offset++;
+ XFS_PVT(inode)->i_leaf_ent_offset = 0;
+ goto try_next_btree;
+ } else {
+ XFS_PVT(inode)->i_leaf_ent_offset++;
+ }
+
+ db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address));
+
+ fsblkno = xfs_dir2_get_right_blk(fs, core, db, &error);
+ if (error) {
+ xfs_error("Cannot find data block!");
+ goto out;
+ }
+
+ buf = xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
+ data_hdr = (xfs_dir2_data_hdr_t *)buf;
+ if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+ xfs_error("Leaf directory's data magic No. does not match!");
+ goto out;
+ }
+
+ offset = xfs_dir2_dataptr_to_off(fs, be32_to_cpu(lep->address));
+
+ dep = (xfs_dir2_data_entry_t *)((uint8_t *)buf + offset);
+
+ start_name = &dep->name[0];
+ end_name = start_name + dep->namelen;
+
+ retval = fill_dirent(fs, dirent, 0, be64_to_cpu(dep->inumber),
+ (char *)start_name, end_name - start_name);
+ if (retval)
+ xfs_error("Failed to fill in dirent structure");
+
+ return retval;
+
+out:
+ xfs_dir2_dirblks_flush_cache();
+
+ XFS_PVT(inode)->i_btree_offset = 0;
+ XFS_PVT(inode)->i_leaf_ent_offset = 0;
+
+ return -1;
+}
diff --git a/core/fs/xfs/xfs_readdir.h b/core/fs/xfs/xfs_readdir.h
new file mode 100644
index 00000000..2e564ec8
--- /dev/null
+++ b/core/fs/xfs/xfs_readdir.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_READDIR_H_
+#define XFS_READDIR_H_
+
+int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core);
+int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core);
+int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core);
+int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent,
+ xfs_dinode_t *core);
+
+#endif /* XFS_READDIR_H_ */
diff --git a/core/fs/xfs/xfs_sb.h b/core/fs/xfs/xfs_sb.h
new file mode 100644
index 00000000..12024ab3
--- /dev/null
+++ b/core/fs/xfs/xfs_sb.h
@@ -0,0 +1,206 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef XFS_SB_H_
+#define XFS_SB_H_
+
+#include <stddef.h>
+
+#include <sys/types.h>
+
+typedef unsigned char uuid_t[16];
+
+/*
+ * Super block
+ * Fits into a sector-sized buffer at address 0 of each allocation group.
+ * Only the first of these is ever updated except during growfs.
+ */
+
+struct xfs_buf;
+struct xfs_mount;
+
+#define XFS_SB_MAGIC "XFSB" /* 'XFSB' */
+#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */
+#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */
+#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */
+#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */
+#define XFS_SB_VERSION_NUMBITS 0x000f
+#define XFS_SB_VERSION_ALLFBITS 0xfff0
+#define XFS_SB_VERSION_SASHFBITS 0xf000
+#define XFS_SB_VERSION_REALFBITS 0x0ff0
+#define XFS_SB_VERSION_ATTRBIT 0x0010
+#define XFS_SB_VERSION_NLINKBIT 0x0020
+#define XFS_SB_VERSION_QUOTABIT 0x0040
+#define XFS_SB_VERSION_ALIGNBIT 0x0080
+#define XFS_SB_VERSION_DALIGNBIT 0x0100
+#define XFS_SB_VERSION_SHAREDBIT 0x0200
+#define XFS_SB_VERSION_LOGV2BIT 0x0400
+#define XFS_SB_VERSION_SECTORBIT 0x0800
+#define XFS_SB_VERSION_EXTFLGBIT 0x1000
+#define XFS_SB_VERSION_DIRV2BIT 0x2000
+#define XFS_SB_VERSION_BORGBIT 0x4000 /* ASCII only case-insens. */
+#define XFS_SB_VERSION_MOREBITSBIT 0x8000
+#define XFS_SB_VERSION_OKSASHFBITS \
+ (XFS_SB_VERSION_EXTFLGBIT | \
+ XFS_SB_VERSION_DIRV2BIT | \
+ XFS_SB_VERSION_BORGBIT)
+#define XFS_SB_VERSION_OKREALFBITS \
+ (XFS_SB_VERSION_ATTRBIT | \
+ XFS_SB_VERSION_NLINKBIT | \
+ XFS_SB_VERSION_QUOTABIT | \
+ XFS_SB_VERSION_ALIGNBIT | \
+ XFS_SB_VERSION_DALIGNBIT | \
+ XFS_SB_VERSION_SHAREDBIT | \
+ XFS_SB_VERSION_LOGV2BIT | \
+ XFS_SB_VERSION_SECTORBIT | \
+ XFS_SB_VERSION_MOREBITSBIT)
+#define XFS_SB_VERSION_OKREALBITS \
+ (XFS_SB_VERSION_NUMBITS | \
+ XFS_SB_VERSION_OKREALFBITS | \
+ XFS_SB_VERSION_OKSASHFBITS)
+
+/*
+ * There are two words to hold XFS "feature" bits: the original
+ * word, sb_versionnum, and sb_features2. Whenever a bit is set in
+ * sb_features2, the feature bit XFS_SB_VERSION_MOREBITSBIT must be set.
+ *
+ * These defines represent bits in sb_features2.
+ */
+#define XFS_SB_VERSION2_REALFBITS 0x00ffffff /* Mask: features */
+#define XFS_SB_VERSION2_RESERVED1BIT 0x00000001
+#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
+#define XFS_SB_VERSION2_RESERVED4BIT 0x00000004
+#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
+#define XFS_SB_VERSION2_PARENTBIT 0x00000010 /* parent pointers */
+#define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32 bit project id */
+
+#define XFS_SB_VERSION2_OKREALFBITS \
+ (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
+ XFS_SB_VERSION2_ATTR2BIT | \
+ XFS_SB_VERSION2_PROJID32BIT)
+#define XFS_SB_VERSION2_OKSASHFBITS \
+ (0)
+#define XFS_SB_VERSION2_OKREALBITS \
+ (XFS_SB_VERSION2_OKREALFBITS | \
+ XFS_SB_VERSION2_OKSASHFBITS )
+
+/*
+ * Sequence number values for the fields.
+ */
+typedef enum {
+ XFS_SBS_MAGICNUM, XFS_SBS_BLOCKSIZE, XFS_SBS_DBLOCKS, XFS_SBS_RBLOCKS,
+ XFS_SBS_REXTENTS, XFS_SBS_UUID, XFS_SBS_LOGSTART, XFS_SBS_ROOTINO,
+ XFS_SBS_RBMINO, XFS_SBS_RSUMINO, XFS_SBS_REXTSIZE, XFS_SBS_AGBLOCKS,
+ XFS_SBS_AGCOUNT, XFS_SBS_RBMBLOCKS, XFS_SBS_LOGBLOCKS,
+ XFS_SBS_VERSIONNUM, XFS_SBS_SECTSIZE, XFS_SBS_INODESIZE,
+ XFS_SBS_INOPBLOCK, XFS_SBS_FNAME, XFS_SBS_BLOCKLOG,
+ XFS_SBS_SECTLOG, XFS_SBS_INODELOG, XFS_SBS_INOPBLOG, XFS_SBS_AGBLKLOG,
+ XFS_SBS_REXTSLOG, XFS_SBS_INPROGRESS, XFS_SBS_IMAX_PCT, XFS_SBS_ICOUNT,
+ XFS_SBS_IFREE, XFS_SBS_FDBLOCKS, XFS_SBS_FREXTENTS, XFS_SBS_UQUOTINO,
+ XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN,
+ XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG,
+ XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT,
+ XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2,
+ XFS_SBS_FIELDCOUNT
+} xfs_sb_field_t;
+
+/*
+ * Mask values, defined based on the xfs_sb_field_t values.
+ * Only define the ones we're using.
+ */
+#define XFS_SB_MVAL(x) (1LL << XFS_SBS_ ## x)
+#define XFS_SB_UUID XFS_SB_MVAL(UUID)
+#define XFS_SB_FNAME XFS_SB_MVAL(FNAME)
+#define XFS_SB_ROOTINO XFS_SB_MVAL(ROOTINO)
+#define XFS_SB_RBMINO XFS_SB_MVAL(RBMINO)
+#define XFS_SB_RSUMINO XFS_SB_MVAL(RSUMINO)
+#define XFS_SB_VERSIONNUM XFS_SB_MVAL(VERSIONNUM)
+#define XFS_SB_UQUOTINO XFS_SB_MVAL(UQUOTINO)
+#define XFS_SB_GQUOTINO XFS_SB_MVAL(GQUOTINO)
+#define XFS_SB_QFLAGS XFS_SB_MVAL(QFLAGS)
+#define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN)
+#define XFS_SB_UNIT XFS_SB_MVAL(UNIT)
+#define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH)
+#define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT)
+#define XFS_SB_IFREE XFS_SB_MVAL(IFREE)
+#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS)
+#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2)
+#define XFS_SB_BAD_FEATURES2 XFS_SB_MVAL(BAD_FEATURES2)
+#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT)
+#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1)
+#define XFS_SB_MOD_BITS \
+ (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
+ XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
+ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
+ XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
+ XFS_SB_BAD_FEATURES2)
+
+
+/*
+ * Misc. Flags - warning - these will be cleared by xfs_repair unless
+ * a feature bit is set when the flag is used.
+ */
+#define XFS_SBF_NOFLAGS 0x00 /* no flags set */
+#define XFS_SBF_READONLY 0x01 /* only read-only mounts allowed */
+
+/*
+ * define max. shared version we can interoperate with
+ */
+#define XFS_SB_MAX_SHARED_VN 0
+
+#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS)
+
+/*
+ * end of superblock version macros
+ */
+
+#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR)
+#define XFS_BUF_TO_SBP(bp) ((xfs_dsb_t *)((bp)->b_addr))
+
+#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)XFS_BB_TO_FSBT(mp,d))
+#define XFS_DADDR_TO_FSB(mp,d) XFS_AGB_TO_FSB(mp, \
+ xfs_daddr_to_agno(mp,d), xfs_daddr_to_agbno(mp,d))
+#define XFS_FSB_TO_DADDR(mp,fsbno) XFS_AGB_TO_DADDR(mp, \
+ XFS_FSB_TO_AGNO(mp,fsbno), XFS_FSB_TO_AGBNO(mp,fsbno))
+
+/*
+ * File system sector to basic block conversions.
+ */
+#define XFS_FSS_TO_BB(mp,sec) ((sec) << (mp)->m_sectbb_log)
+
+/*
+ * File system block to basic block conversions.
+ */
+#define XFS_FSB_TO_BB(mp,fsbno) ((fsbno) << (mp)->m_blkbb_log)
+#define XFS_BB_TO_FSB(mp,bb) \
+ (((bb) + (XFS_FSB_TO_BB(mp,1) - 1)) >> (mp)->m_blkbb_log)
+#define XFS_BB_TO_FSBT(mp,bb) ((bb) >> (mp)->m_blkbb_log)
+
+/*
+ * File system block to byte conversions.
+ */
+#define XFS_FSB_TO_B(mp,fsbno) ((xfs_fsize_t)(fsbno) << (mp)->m_sb.sb_blocklog)
+#define XFS_B_TO_FSB(mp,b) \
+ ((((uint64_t)(b)) + (mp)->m_blockmask) >> (mp)->m_sb.sb_blocklog)
+#define XFS_B_TO_FSBT(mp,b) (((uint64_t)(b)) >> (mp)->m_sb.sb_blocklog)
+#define XFS_B_FSB_OFFSET(mp,b) ((b) & (mp)->m_blockmask)
+
+#endif /* XFS_SB_H_ */
diff --git a/core/fs/xfs/xfs_types.h b/core/fs/xfs/xfs_types.h
new file mode 100644
index 00000000..92808865
--- /dev/null
+++ b/core/fs/xfs/xfs_types.h
@@ -0,0 +1,135 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef XFS_TYPES_H_
+#define XFS_TYPES_H_
+
+#include <stddef.h>
+
+#include <sys/types.h>
+
+typedef enum { B_FALSE,B_TRUE } boolean_t;
+typedef uint32_t prid_t; /* project ID */
+typedef uint32_t inst_t; /* an instruction */
+
+typedef int64_t xfs_off_t; /* <file offset> type */
+typedef unsigned long long xfs_ino_t; /* <inode> type */
+typedef int64_t xfs_daddr_t; /* <disk address> type */
+typedef char * xfs_caddr_t; /* <core address> type */
+typedef uint32_t xfs_dev_t;
+typedef uint32_t xfs_nlink_t;
+
+/* __psint_t is the same size as a pointer */
+typedef int32_t __psint_t;
+typedef uint32_t __psunsigned_t;
+
+typedef uint32_t xfs_agblock_t; /* blockno in alloc. group */
+typedef uint32_t xfs_extlen_t; /* extent length in blocks */
+typedef uint32_t xfs_agnumber_t; /* allocation group number */
+typedef int32_t xfs_extnum_t; /* # of extents in a file */
+typedef int16_t xfs_aextnum_t; /* # extents in an attribute fork */
+typedef int64_t xfs_fsize_t; /* bytes in a file */
+typedef uint64_t xfs_ufsize_t; /* unsigned bytes in a file */
+
+typedef int32_t xfs_suminfo_t; /* type of bitmap summary info */
+typedef int32_t xfs_rtword_t; /* word type for bitmap manipulations */
+
+typedef int64_t xfs_lsn_t; /* log sequence number */
+typedef int32_t xfs_tid_t; /* transaction identifier */
+
+typedef uint32_t xfs_dablk_t; /* dir/attr block number (in file) */
+typedef uint32_t xfs_dahash_t; /* dir/attr hash value */
+
+/*
+ * These types are 64 bits on disk but are either 32 or 64 bits in memory.
+ * Disk based types:
+ */
+typedef uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */
+typedef uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */
+typedef uint64_t xfs_drtbno_t; /* extent (block) in realtime area */
+typedef uint64_t xfs_dfiloff_t; /* block number in a file */
+typedef uint64_t xfs_dfilblks_t; /* number of blocks in a file */
+
+/*
+ * Memory based types are conditional.
+ */
+typedef uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */
+typedef uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */
+typedef uint64_t xfs_rtblock_t; /* extent (block) in realtime area */
+typedef int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */
+
+typedef uint64_t xfs_fileoff_t; /* block number in a file */
+typedef int64_t xfs_sfiloff_t; /* signed block number in a file */
+typedef uint64_t xfs_filblks_t; /* number of blocks in a file */
+
+/*
+ * Null values for the types.
+ */
+#define NULLDFSBNO ((xfs_dfsbno_t)-1)
+#define NULLDRFSBNO ((xfs_drfsbno_t)-1)
+#define NULLDRTBNO ((xfs_drtbno_t)-1)
+#define NULLDFILOFF ((xfs_dfiloff_t)-1)
+
+#define NULLFSBLOCK ((xfs_fsblock_t)-1)
+#define NULLRFSBLOCK ((xfs_rfsblock_t)-1)
+#define NULLRTBLOCK ((xfs_rtblock_t)-1)
+#define NULLFILEOFF ((xfs_fileoff_t)-1)
+
+#define NULLAGBLOCK ((xfs_agblock_t)-1)
+#define NULLAGNUMBER ((xfs_agnumber_t)-1)
+#define NULLEXTNUM ((xfs_extnum_t)-1)
+
+#define NULLCOMMITLSN ((xfs_lsn_t)-1)
+
+/*
+ * Max values for extlen, extnum, aextnum.
+ */
+#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */
+#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */
+#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */
+
+/*
+ * Min numbers of data/attr fork btree root pointers.
+ */
+#define MINDBTPTRS 3
+#define MINABTPTRS 2
+
+/*
+ * MAXNAMELEN is the length (including the terminating null) of
+ * the longest permissible file (component) name.
+ */
+#define MAXNAMELEN 256
+
+typedef enum {
+ XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi
+} xfs_lookup_t;
+
+typedef enum {
+ XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi,
+ XFS_BTNUM_MAX
+} xfs_btnum_t;
+
+struct xfs_name {
+ const unsigned char *name;
+ int len;
+};
+
+#endif /* XFS_TYPES_H_ */
diff --git a/core/getc.inc b/core/getc.inc
deleted file mode 100644
index 33656b40..00000000
--- a/core/getc.inc
+++ /dev/null
@@ -1,415 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; getc.inc
-;;
-;; Simple file handling library (open, getc, ungetc)
-;;
-;; WARNING: This interface uses the real_mode_seg/comboot_seg.
-;;
-
-MAX_GETC_LG2 equ 4 ; Max number of file nesting
-MAX_GETC equ (1 << MAX_GETC_LG2)
-bytes_per_getc_lg2 equ 16-MAX_GETC_LG2
-bytes_per_getc equ (1 << bytes_per_getc_lg2)
-MAX_UNGET equ 9 ; Max bytes that can be pushed back
-
- struc getc_file
-gc_file resw 1 ; File pointer
-gc_bufbytes resw 1 ; Bytes left in buffer
-gc_bufdata resw 1 ; Pointer to data in buffer
-gc_unget_cnt resb 1 ; Character pushed back count
-gc_unget_buf resb MAX_UNGET ; Character pushed back buffer
- endstruc
-getc_file_lg2 equ 4 ; Size of getc_file as a power of 2
-
-%ifndef DEPEND
-%if (getc_file_size != (1 << getc_file_lg2))
-%error "getc_file_size != (1 << getc_file_lg2)"
-%endif
-%endif
-
-;
-; open,getc: Load a file a character at a time for parsing in a manner
-; similar to the C library getc routine.
-; Up to MAX_GETC files can be open at the same time,
-; they are accessed in a stack-like fashion.
-;
-; All routines assume CS == DS.
-;
-; open: Input: mangled filename in DS:DI
-; Output: ZF set on file not found or zero length
-;
-; openfd: Input: file handle in SI, file size in EAX
-; Output: ZF set on getc stack overflow
-;
-; getc: Output: CF set on end of file
-; Character loaded in AL
-;
-; close: Output: CF set if nothing open
-;
- global core_open
-core_open:
- pm_call pm_searchdir
- jz openfd.ret
-openfd:
- push bx
-
- mov bx,[CurrentGetC]
- sub bx,getc_file_size
- cmp bx,GetCStack
- jb .stack_full ; Excessive nesting
- mov [CurrentGetC],bx
-
- mov [bx+gc_file],si ; File pointer
- xor ax,ax
- mov [bx+gc_bufbytes],ax ; Buffer empty
- mov [bx+gc_unget_cnt],al ; ungetc buffer empty
-
- inc ax ; ZF <- 0
- pop bx
-.ret: ret
-
-.stack_full:
- pm_call pm_close_file
- xor ax,ax ; ZF <- 1
- pop bx
- ret
-
-getc:
- push bx
- push si
- push di
- push es
-
- mov di,[CurrentGetC]
- movzx bx,byte [di+gc_unget_cnt]
- and bx,bx
- jnz .have_unget
-
- mov si,real_mode_seg ; Borrow the real_mode_seg
- mov es,si
-
-.got_data:
- sub word [di+gc_bufbytes],1
- jc .get_data ; Was it zero already?
- mov si,[di+gc_bufdata]
- mov al,[es:si]
- inc si
- mov [di+gc_bufdata],si
-.done:
- clc
-.ret:
- pop es
- pop di
- pop si
- pop bx
- ret
-.have_unget:
- dec bx
- mov al,[di+bx+gc_unget_buf]
- mov [di+gc_unget_cnt],bl
- jmp .done
-
-.get_data:
- pushad
- ; Compute start of buffer
- mov bx,di
- sub bx,GetCStack
- shl bx,bytes_per_getc_lg2-getc_file_lg2
-
- mov [di+gc_bufdata],bx
- mov si,[di+gc_file]
- and si,si
- mov [di+gc_bufbytes],si ; In case SI == 0
- jz .empty
- mov cx,bytes_per_getc
- pm_call getfsbytes
- mov [di+gc_bufbytes],cx
- mov [di+gc_file],si
- jcxz .empty
- popad
- TRACER 'd'
- jmp .got_data
-
-.empty:
- TRACER 'e'
- ; [di+gc_bufbytes] is zero already, thus we will continue
- ; to get EOF on any further attempts to read the file.
- popad
- xor al,al ; Return a predictable zero
- stc
- jmp .ret
-
-;
-; This is similar to getc, except that we read up to CX bytes and
-; store them in ES:DI. Eventually this could get optimized...
-;
-; On return, CX and DI are adjusted by the number of bytes actually read.
-;
-readc:
- push ax
-.loop:
- call getc
- jc .out
- stosb
- loop .loop
-.out:
- pop ax
- ret
-
-;
-; close: close the top of the getc stack
-;
-close:
- push bx
- push si
- mov bx,[CurrentGetC]
- mov si,[bx+gc_file]
- pm_call pm_close_file
- add bx,getc_file_size
- mov [CurrentGetC],bx
- pop si
- pop bx
- ret
-
-;
-; ungetc: Push a character (in AL) back into the getc buffer
-; Note: if more than MAX_UNGET bytes are pushed back, all
-; hell will break loose.
-;
-ungetc:
- push di
- push bx
- mov di,[CurrentGetC]
- movzx bx,[di+gc_unget_cnt]
- mov [bx+di+gc_unget_buf],al
- inc bx
- mov [di+gc_unget_cnt],bl
- pop bx
- pop di
- ret
-
-;
-; skipspace: Skip leading whitespace using "getc". If we hit end-of-line
-; or end-of-file, return with carry set; ZF = true of EOF
-; ZF = false for EOLN; otherwise CF = ZF = 0.
-;
-; Otherwise AL = first character after whitespace
-;
-skipspace:
-.loop: call getc
- jc .eof
- cmp al,1Ah ; DOS EOF
- je .eof
- cmp al,0Ah
- je .eoln
- cmp al,' '
- jbe .loop
- ret ; CF = ZF = 0
-.eof: cmp al,al ; Set ZF
- stc ; Set CF
- ret
-.eoln: add al,0FFh ; Set CF, clear ZF
- ret
-
-;
-; getint: Load an integer from the getc file.
-; Return CF if error; otherwise return integer in EBX
-;
-getint:
- mov di,NumBuf
-.getnum: cmp di,NumBufEnd ; Last byte in NumBuf
- jae .loaded
- push di
- call getc
- pop di
- jc .loaded
- stosb
- cmp al,'-'
- jnb .getnum
- call ungetc ; Unget non-numeric
-.loaded: mov byte [di],0
- mov si,NumBuf
- ; Fall through to parseint
-;
-; parseint: Convert an integer to a number in EBX
-; Get characters from string in DS:SI
-; Return CF on error
-; DS:SI points to first character after number
-;
-; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+[KMG]
-;
-parseint:
- push eax
- push ecx
- push bp
- xor eax,eax ; Current digit (keep eax == al)
- mov ebx,eax ; Accumulator
- mov ecx,ebx ; Base
- xor bp,bp ; Used for negative flag
-.begin: lodsb
- cmp al,'-'
- jne .not_minus
- xor bp,1 ; Set unary minus flag
- jmp short .begin
-.not_minus:
- cmp al,'0'
- jb .err
- je .octhex
- cmp al,'9'
- ja .err
- mov cl,10 ; Base = decimal
- jmp short .foundbase
-.octhex:
- lodsb
- cmp al,'0'
- jb .km ; Value is zero
- or al,20h ; Downcase
- cmp al,'x'
- je .ishex
- cmp al,'7'
- ja .err
- mov cl,8 ; Base = octal
- jmp short .foundbase
-.ishex:
- mov al,'0' ; No numeric value accrued yet
- mov cl,16 ; Base = hex
-.foundbase:
- call unhexchar
- jc .km ; Not a (hex) digit
- cmp al,cl
- jae .km ; Invalid for base
- imul ebx,ecx ; Multiply accumulated by base
- add ebx,eax ; Add current digit
- lodsb
- jmp short .foundbase
-.km:
- dec si ; Back up to last non-numeric
- lodsb
- or al,20h
- cmp al,'k'
- je .isk
- cmp al,'m'
- je .ism
- cmp al,'g'
- je .isg
- dec si ; Back up
-.fini: and bp,bp
- jz .ret ; CF=0!
- neg ebx ; Value was negative
-.done: clc
-.ret: pop bp
- pop ecx
- pop eax
- ret
-.err: stc
- jmp short .ret
-.isg: shl ebx,10 ; * 2^30
-.ism: shl ebx,10 ; * 2^20
-.isk: shl ebx,10 ; * 2^10
- jmp .fini
-
- section .bss16
- alignb 4
-NumBuf resb 15 ; Buffer to load number
-NumBufEnd resb 1 ; Last byte in NumBuf
-
-GetCStack resb getc_file_size*MAX_GETC
-.end equ $
-
- section .data16
-CurrentGetC dw GetCStack.end ; GetCStack empty
-
-;
-; unhexchar: Convert a hexadecimal digit in AL to the equivalent number;
-; return CF=1 if not a hex digit
-;
- section .text16
-unhexchar:
- cmp al,'0'
- jb .ret ; If failure, CF == 1 already
- cmp al,'9'
- ja .notdigit
- sub al,'0' ; CF <- 0
- ret
-.notdigit: or al,20h ; upper case -> lower case
- cmp al,'a'
- jb .ret ; If failure, CF == 1 already
- cmp al,'f'
- ja .err
- sub al,'a'-10 ; CF <- 0
- ret
-.err: stc
-.ret: ret
-
-;
-;
-; getline: Get a command line, converting control characters to spaces
-; and collapsing streches to one; a space is appended to the
-; end of the string, unless the line is empty.
-; The line is terminated by ^J, ^Z or EOF and is written
-; to ES:DI. On return, DI points to first char after string.
-; CF is set if we hit EOF.
-;
-getline:
- call skipspace
- mov dl,1 ; Empty line -> empty string.
- jz .eof ; eof
- jc .eoln ; eoln
- call ungetc
-.fillloop: push dx
- push di
- call getc
- pop di
- pop dx
- jc .ret ; CF set!
- cmp al,' '
- jna .ctrl
- xor dx,dx
-.store: stosb
- jmp short .fillloop
-.ctrl: cmp al,10
- je .ret ; CF clear!
- cmp al,26
- je .eof
- and dl,dl
- jnz .fillloop ; Ignore multiple spaces
- mov al,' ' ; Ctrl -> space
- inc dx
- jmp short .store
-.eoln: clc ; End of line is not end of file
- jmp short .ret
-.eof: stc
-.ret: pushf ; We want the last char to be space!
- and dl,dl
- jnz .xret
- mov al,' '
- stosb
-.xret: popf
- ret
-
-;
-; parseint_esdi:
-; Same as parseint, but takes the input in ES:DI
-;
-parseint_esdi:
- push ds
- push es
- pop ds
- xchg si,di
- call parseint
- xchg si,di
- pop ds
- ret
diff --git a/core/graphics.c b/core/graphics.c
new file mode 100644
index 00000000..834372ff
--- /dev/null
+++ b/core/graphics.c
@@ -0,0 +1,373 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ *
+ * -----------------------------------------------------------------------
+ * VGA splash screen code
+ * -----------------------------------------------------------------------
+ */
+
+#include <stddef.h>
+#include "core.h"
+#include <sys/io.h>
+#include <hw/vga.h>
+#include "fs.h"
+
+#include "bios.h"
+#include "graphics.h"
+
+__export uint8_t UsingVGA = 0;
+uint16_t VGAPos; /* Pointer into VGA memory */
+__export uint16_t *VGAFilePtr; /* Pointer into VGAFileBuf */
+__export uint16_t VGAFontSize = 16; /* Defaults to 16 byte font */
+
+__export char VGAFileBuf[VGA_FILE_BUF_SIZE]; /* Unmangled VGA image name */
+__export char VGAFileMBuf[FILENAME_MAX]; /* Mangled VGA image name */
+
+static uint8_t VGARowBuffer[640 + 80]; /* Decompression buffer */
+static uint8_t VGAPlaneBuffer[(640/8) * 4]; /* Plane buffers */
+
+extern uint16_t GXPixCols;
+extern uint16_t GXPixRows;
+
+/* Maps colors to consecutive DAC registers */
+static uint8_t linear_color[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 0 };
+
+static FILE *fd;
+
+typedef struct {
+ uint32_t LSSMagic; /* Magic number */
+ uint16_t GraphXSize; /* Width of splash screen file */
+ uint16_t GraphYSize; /* Height of splash screen file */
+ uint8_t GraphColorMap[3*16];
+} lssheader_t;
+
+static lssheader_t LSSHeader;
+
+#define LSSMagic LSSHeader.LSSMagic
+#define GraphXSize LSSHeader.GraphXSize
+#define GraphYSize LSSHeader.GraphYSize
+
+/*
+ * Enable VGA graphics, if possible. Return 0 on success.
+ */
+static int vgasetmode(void)
+{
+ com32sys_t ireg, oreg;
+
+ if (UsingVGA)
+ return 0; /* Nothing to do... */
+
+ memset(&ireg, 0, sizeof(ireg));
+ memset(&oreg, 0, sizeof(oreg));
+
+ if (UsingVGA & 0x4) {
+ /*
+ * We're in VESA mode, which means VGA; use VESA call
+ * to revert the mode, and then call the conventional
+ * mode-setting for good measure...
+ */
+ ireg.eax.w[0] = 0x4F02;
+ ireg.ebx.w[0] = 0x0012;
+ __intcall(0x10, &ireg, &oreg);
+ } else {
+ /* Get video card and monitor */
+ ireg.eax.w[0] = 0x1A00;
+ __intcall(0x10, &ireg, &oreg);
+ oreg.ebx.b[0] -= 7; /* BL=07h and BL=08h OK */
+
+ if (oreg.ebx.b[0] > 1)
+ return -1;
+ }
+
+ /*
+ * Set mode.
+ */
+ ireg.eax.w[0] = 0x0012; /* Set mode = 640x480 VGA 16 colors */
+ __intcall(0x10, &ireg, &oreg);
+
+ ireg.edx.w[0] = (uint32_t)linear_color;
+ ireg.eax.w[0] = 0x1002; /* Write color registers */
+ __intcall(0x10, &ireg, &oreg);
+
+ UsingVGA = 1;
+
+ /* Set GXPixCols and GXPixRows */
+ GXPixCols = 640;
+ GXPixRows = 480;
+
+ use_font();
+ ScrollAttribute = 0;
+
+ return 0;
+}
+
+static inline char getnybble(void)
+{
+ char data = getc(fd);
+
+ if (data & 0x10) {
+ data &= 0x0F;
+ return data;
+ }
+
+ data = getc(fd);
+ return (data & 0x0F);
+}
+
+/*
+ * rledecode:
+ * Decode a pixel row in RLE16 format.
+ *
+ * 'in': input (RLE16 encoded) buffer
+ * 'out': output (decoded) buffer
+ * 'count': pixel count
+ */
+static void rledecode(uint8_t *out, size_t count)
+{
+ uint8_t prev_pixel = 0;
+ size_t size = count;
+ uint8_t data;
+ int i;
+
+again:
+ for (i = 0; i < size; i++) {
+
+ data = getnybble();
+ if (data == prev_pixel)
+ break;
+
+ *out++ = data;
+ prev_pixel = data;
+ }
+
+ size -= i;
+ if (!size)
+ return;
+
+ /* Start of run sequence */
+ data = getnybble();
+ if (data == 0) {
+ /* long run */
+ uint8_t hi;
+
+ data = getnybble();
+ hi = getnybble();
+ hi <<= 4;
+ data |= hi;
+ data += 16;
+ }
+
+ /* dorun */
+ for (i = 0; i < data; i++)
+ *out++ = prev_pixel;
+
+ size -= i;
+ if (size)
+ goto again;
+}
+
+/*
+ * packedpixel2vga:
+ * Convert packed-pixel to VGA bitplanes
+ *
+ * 'in': packed pixel string (640 pixels)
+ * 'out': output (four planes @ 640/8 = 80 bytes)
+ * 'count': pixel count (multiple of 8)
+ */
+static void packedpixel2vga(const uint8_t *in, uint8_t *out)
+{
+ int i, j, k;
+
+ for (i = 0; i < 4; i++) {
+ const uint8_t *ip = in;
+
+ for (j = 0; j < 640/8; j++) {
+ uint8_t ob = 0;
+
+ for (k = 0; k < 8; k++) {
+ uint8_t px = *ip++;
+ ob = (ob << 1) | ((px >> i) & 1);
+ }
+
+ *out++ = ob;
+ }
+ }
+}
+
+/*
+ * outputvga:
+ * Output four subsequent lines of VGA data
+ *
+ * 'in': four planes @ 640/8=80 bytes
+ * 'out': pointer into VGA memory
+ */
+static void outputvga(const void *in, void *out)
+{
+ int i;
+
+ /* Select the sequencer mask */
+ outb(VGA_SEQ_IX_MAP_MASK, VGA_SEQ_ADDR);
+
+ for (i = 1; i <= 8; i <<= 1) {
+ /* Select the bit plane to write */
+ outb(i, VGA_SEQ_DATA);
+ memcpy(out, in, 640/8);
+ in = (const char *)in + 640/8;
+ }
+}
+
+/*
+ * Display a graphical splash screen.
+ */
+__export void vgadisplayfile(FILE *_fd)
+{
+ char *p;
+ int size;
+
+ fd = _fd;
+
+ /*
+ * This is a cheap and easy way to make sure the screen is
+ * cleared in case we were in graphics mode aready.
+ */
+ syslinux_force_text_mode();
+ vgasetmode();
+
+ size = 4+2*2+16*3;
+ p = (char *)&LSSHeader;
+
+ /* Load the header */
+ while (size--)
+ *p = getc(fd);
+
+ if (*p != EOF) {
+ com32sys_t ireg, oreg;
+ uint16_t rows;
+ int i;
+
+ /* The header WILL be in the first chunk. */
+ if (LSSMagic != 0x1413f33d)
+ return;
+
+ memset(&ireg, 0, sizeof(ireg));
+
+ /* Color map offset */
+ ireg.edx.w[0] = offsetof(lssheader_t, GraphColorMap);
+
+ ireg.eax.w[0] = 0x1012; /* Set RGB registers */
+ ireg.ebx.w[0] = 0; /* First register number */
+ ireg.ecx.w[0] = 16; /* 16 registers */
+ __intcall(0x10, &ireg, &oreg);
+
+ /* Number of pixel rows */
+ rows = (GraphYSize + VGAFontSize) - 1;
+ rows = rows / VGAFontSize;
+ if (rows >= VidRows)
+ rows = VidRows - 1;
+
+ memset(&ireg, 0, sizeof(ireg));
+
+ ireg.edx.b[1] = rows;
+ ireg.eax.b[1] = 2;
+ ireg.ebx.w[0] = 0;
+
+ /* Set cursor below image */
+ __intcall(0x10, &ireg, &oreg);
+
+ rows = GraphYSize; /* Number of graphics rows */
+ VGAPos = 0;
+
+ for (i = 0; i < rows; i++) {
+ /* Pre-clear the row buffer */
+ memset(VGARowBuffer, 0, 640);
+
+ /* Decode one row */
+ rledecode(VGARowBuffer, GraphXSize);
+
+ packedpixel2vga(VGARowBuffer, VGAPlaneBuffer);
+ outputvga(VGAPlaneBuffer, MK_PTR(0xA000, VGAPos));
+ VGAPos += 640/8;
+ }
+ }
+}
+
+/*
+ * Disable VGA graphics.
+ */
+__export void syslinux_force_text_mode(void)
+{
+ com32sys_t ireg, oreg;
+
+ /* Already in text mode? */
+ if (!UsingVGA)
+ return;
+
+ if (UsingVGA & 0x4) {
+ /* VESA return to normal video mode */
+ memset(&ireg, 0, sizeof(ireg));
+
+ ireg.eax.w[0] = 0x4F02; /* Set SuperVGA video mode */
+ ireg.ebx.w[0] = 0x0003;
+ __intcall(0x10, &ireg, &oreg);
+ }
+
+ /* Return to normal video mode */
+ memset(&ireg, 0, sizeof(ireg));
+ ireg.eax.w[0] = 0x0003;
+ __intcall(0x10, &ireg, &oreg);
+
+ UsingVGA = 0;
+
+ ScrollAttribute = 0x7;
+ /* Restore text font/data */
+ use_font();
+}
+
+static void vgacursorcommon(char data)
+{
+ if (UsingVGA) {
+ com32sys_t ireg;
+
+ ireg.eax.b[0] = data;
+ ireg.eax.b[1] = 0x09;
+ ireg.ebx.w[0] = 0x0007;
+ ireg.ecx.w[0] = 1;
+ __intcall(0x10, &ireg, NULL);
+ }
+}
+
+void vgahidecursor(void)
+{
+ vgacursorcommon(' ');
+}
+
+void vgashowcursor(void)
+{
+ vgacursorcommon('_');
+}
+
+__export void using_vga(uint8_t vga, uint16_t pix_cols, uint16_t pix_rows)
+{
+ UsingVGA = vga;
+ GXPixCols = pix_cols;
+ GXPixRows = pix_rows;
+
+ if (!(UsingVGA & 0x08))
+ adjust_screen();
+}
+
+void pm_using_vga(com32sys_t *regs)
+{
+ using_vga(regs->eax.b[0], regs->ecx.w[0], regs->edx.w[0]);
+}
diff --git a/core/graphics.inc b/core/graphics.inc
deleted file mode 100644
index a8d28515..00000000
--- a/core/graphics.inc
+++ /dev/null
@@ -1,353 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-; ----------------------------------------------------------------------------
-; VGA splash screen code
-; ----------------------------------------------------------------------------
-
-;
-; vgadisplayfile:
-; Display a graphical splash screen.
-; The file is already opened on the top of the getc stack.
-;
-; Assumes CS == DS == ES.
-;
- section .text16
-
-vgadisplayfile:
- ; This is a cheap and easy way to make sure the screen is
- ; cleared in case we were in graphics mode already
- call vgaclearmode
- call vgasetmode
- jnz .error_nz
-
-.graphalready:
- ; Load the header.
- mov cx,4+2*2+16*3
- mov di,LSSHeader
-.gethdr:
- call getc
- stosb
- loop .gethdr
- jc .error
-
- ; The header WILL be in the first chunk.
- cmp dword [LSSMagic],0x1413f33d ; Magic number
-.error_nz: jne .error
-
- mov dx,GraphColorMap ; Color map offset
- mov ax,1012h ; Set RGB registers
- xor bx,bx ; First register number
- mov cx,16 ; 16 registers
- int 10h
-
-.movecursor:
- mov ax,[GraphYSize] ; Number of pixel rows
- mov dx,[VGAFontSize]
- add ax,dx
- dec ax
- div dl
- xor dx,dx ; Set column to 0
- cmp al,[VidRows]
- jb .rowsok
- mov al,[VidRows]
- dec al
-.rowsok:
- mov dh,al
- mov ah,2
- xor bx,bx
- int 10h ; Set cursor below image
-
- mov cx,[GraphYSize] ; Number of graphics rows
- mov word [VGAPos],0
-
-.drawpixelrow:
- push cx
- mov di,VGARowBuffer
- ; Pre-clear the row buffer
- push di
- push di
- mov cx,640/4
- xor eax,eax
- rep stosd
- pop di
- mov cx,[GraphXSize]
- call rledecode ; Decode one row
- pop si
- mov di,VGAPlaneBuffer
- push di
- mov bp,640
- call packedpixel2vga
- pop si
- push es
- mov di,0A000h ; VGA segment
- mov es,di
- mov di,[VGAPos]
- call outputvga
- pop es
- add word [VGAPos],640/8
- pop cx
- loop .drawpixelrow
-
-.error:
- jmp close ; Tailcall!
-
-;
-; rledecode:
-; Decode a pixel row in RLE16 format.
-;
-; getc stack -> input
-; CX -> pixel count
-; ES:DI -> output (packed pixel)
-;
-rledecode:
- xor dx,dx ; DL = last pixel, DH = nybble buffer
-.loop:
- call .getnybble
- cmp al,dl
- je .run ; Start of run sequence
- stosb
- mov dl,al
- dec cx
- jnz .loop
-.done:
- ret
-.run:
- xor bx,bx
- call .getnybble
- or bl,al
- jz .longrun
-.dorun:
- push cx
- mov cx,bx
- mov al,dl
- rep stosb
- pop cx
- sub cx,bx
- ja .loop
- jmp short .done
-.longrun:
- call .getnybble
- mov bl,al
- call .getnybble
- shl al,4
- or bl,al
- add bx,16
- jmp short .dorun
-
-.getnybble:
- test dh,10h
- jz .low
- and dh,0Fh
- mov al,dh
- ret
-.low:
- call getc
- mov dh,al
- shr dh,4
- or dh,10h ; Nybble already read
- and al,0Fh
- ret
-
-;
-; packedpixel2vga:
-; Convert packed-pixel to VGA bitplanes
-;
-; DS:SI -> packed pixel string
-; BP -> pixel count (multiple of 8)
-; DS:DI -> output (four planes)
-;
-packedpixel2vga:
- xor cx,cx
-.planeloop:
- inc cx
- push si
- push bp
-.loop1:
- mov bx,8
-.loop2:
- lodsb
- shr al,cl
- rcl dl,1 ; VGA is bigendian. Sigh.
- dec bx
- jnz .loop2
- mov [di],dl
- inc di
- sub bp,byte 8
- ja .loop1
- pop bp
- pop si
- cmp cl,3
- jbe .planeloop
- ret
-
-;
-; outputvga:
-; Output four subsequent lines of VGA data
-;
-; DS:SI -> four planes @ 640/8=80 bytes
-; ES:DI -> pointer into VGA memory
-;
-outputvga:
- mov dx,3C4h ; VGA Sequencer Register select port
- mov al,2 ; Sequencer mask
- out dx,al ; Select the sequencer mask
- inc dx ; VGA Sequencer Register data port
- dec ax ; AL <- 1
-.loop1:
- out dx,al ; Select the bit plane to write
- push di
- mov cx,640/32
- rep movsd
- pop di
- add ax,ax
- cmp al,8
- jbe .loop1
- ret
-
-;
-; vgasetmode:
-; Enable VGA graphics, if possible; return ZF=1 on success
-; DS must be set to the base segment; ES is set to DS.
-;
-vgasetmode:
- push ds
- pop es
- mov al,[UsingVGA]
- cmp al,01h
- je .success ; Nothing to do...
- test al,04h
- jz .notvesa
- ; We're in a VESA mode, which means VGA; use VESA call
- ; to revert the mode, and then call the conventional
- ; mode-setting for good measure...
- mov ax,4F02h
- mov bx,0012h
- int 10h
- jmp .setmode
-.notvesa:
- mov ax,1A00h ; Get video card and monitor
- xor bx,bx
- int 10h
- sub bl, 7 ; BL=07h and BL=08h OK
- cmp bl, 1
- ja .error ; ZF=0
-; mov bx,TextColorReg
-; mov dx,1009h ; Read color registers
-; int 10h
-.setmode:
- mov ax,0012h ; Set mode = 640x480 VGA 16 colors
- int 10h
- mov dx,linear_color
- mov ax,1002h ; Write color registers
- int 10h
- mov [UsingVGA], byte 1
-
- ; Set GXPixCols and GXPixRows
- mov dword [GXPixCols],640+(480 << 16)
-
- call use_font ; Set graphics font/data
- mov byte [ScrollAttribute], 00h
-
-.success:
- xor ax,ax ; Set ZF
-.error:
- ret
-
-;
-; vgaclearmode:
-; Disable VGA graphics. It is not safe to assume any value
-; for DS or ES.
-;
-vgaclearmode:
- push ds
- push es
- pushad
- mov ax,cs
- mov ds,ax
- mov es,ax
- mov al,[UsingVGA]
- and al,al ; Already in text mode?
- jz .done
- test al,04h
- jz .notvesa
- mov ax,4F02h ; VESA return to normal video mode
- mov bx,0003h
- int 10h
-.notvesa:
- mov ax,0003h ; Return to normal video mode
- int 10h
-; mov dx,TextColorReg ; Restore color registers
-; mov ax,1002h
-; int 10h
- mov [UsingVGA], byte 0
-
- mov byte [ScrollAttribute], 07h
- call use_font ; Restore text font/data
-.done:
- popad
- pop es
- pop ds
- ret
-
-;
-; vgashowcursor/vgahidecursor:
-; If VGA graphics is enabled, draw a cursor/clear a cursor
-;
-vgashowcursor:
- pushad
- mov al,'_'
- jmp short vgacursorcommon
-vgahidecursor:
- pushad
- mov al,' '
-vgacursorcommon:
- cmp [UsingVGA], byte 1
- jne .done
- mov ah,09h
- mov bx,0007h
- mov cx,1
- int 10h
-.done:
- popad
- ret
-
-
- section .data16
- ; Map colors to consecutive DAC registers
-linear_color db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
-
- ; See comboot.doc, INT 22h AX=0017h for the semantics
- ; of this byte.
-UsingVGA db 0
-
- section .bss16
- alignb 4
-LSSHeader equ $
-LSSMagic resd 1 ; Magic number
-GraphXSize resw 1 ; Width of splash screen file
-GraphYSize resw 1 ; Height of splash screen file
-GraphColorMap resb 3*16
-VGAPos resw 1 ; Pointer into VGA memory
-VGAFilePtr resw 1 ; Pointer into VGAFileBuf
-; TextColorReg resb 17 ; VGA color registers for text mode
-%if IS_SYSLINUX
-VGAFileBuf resb FILENAME_MAX+2 ; Unmangled VGA image name
-%else
-VGAFileBuf resb FILENAME_MAX ; Unmangled VGA image name
-%endif
-VGAFileBufEnd equ $
-VGAFileMBuf resb FILENAME_MAX ; Mangled VGA image name
-
- alignb 4
-VGARowBuffer resb 640+80 ; Decompression buffer
-VGAPlaneBuffer resb (640/8)*4 ; Plane buffers
diff --git a/core/hello.c b/core/hello.c
new file mode 100644
index 00000000..bed7cb59
--- /dev/null
+++ b/core/hello.c
@@ -0,0 +1,78 @@
+#include <stddef.h>
+#include <com32.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "core.h"
+
+#include <console.h>
+
+void myputchar(int c)
+{
+ if (c == '\n')
+ myputchar('\r');
+
+ writechr(c);
+}
+
+void myputs(const char *str)
+{
+ while (*str)
+ myputchar(*str++);
+}
+
+void hello(void)
+{
+ static char hello_str[] = "Hello, World!";
+
+ printf("%s from (%s)\n", hello_str, __FILE__); /* testing */
+}
+
+void hexdump(void *buf, int bytelen, const char *str)
+{
+ unsigned int *p32, i;
+
+ if (str)
+ printf("Dump %s:\n", str);
+
+ p32 = (unsigned int *)buf;
+ for (i = 0; i < (bytelen / 4); i++){
+ printf(" 0x%08x ", p32[i]);
+ }
+ printf("\n\n");
+}
+
+static inline void myprint(int num)
+{
+ uint32_t i;
+
+ for (i = 0; i < 5; i ++)
+ printf("%d", num);
+ printf("\n");
+}
+
+void mp1(void)
+{
+ myprint(1);
+}
+
+void mp2(void)
+{
+ myprint(2);
+}
+
+void mp3(void)
+{
+ myprint(3);
+}
+
+void mp4(void)
+{
+ myprint(4);
+}
+
+void mp5(void)
+{
+ myprint(5);
+}
+
diff --git a/core/highmem.inc b/core/highmem.inc
deleted file mode 100644
index ea386ffc..00000000
--- a/core/highmem.inc
+++ /dev/null
@@ -1,158 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; highmem.inc
-;;
-;; Probe for the size of high memory. This can be overridden by a
-;; mem= command on the command line while booting a new kernel.
-;;
-
- section .text16
-
-;
-; This is set up as a subroutine; it will set up the global variable
-; HighMemSize. All registers are preserved.
-;
-highmemsize:
- push es
- pushfd
- pushad
-
- push cs
- pop es
-
-;
-; First, try INT 15:E820 (get BIOS memory map)
-;
-; Note: we may have to scan this multiple times, because some (daft) BIOSes
-; report main memory as multiple contiguous ranges...
-;
-get_e820:
- mov dword [E820Max],-(1 << 20) ; Max amount of high memory
- mov dword [E820Mem],(1 << 20) ; End of detected high memory
-.start_over:
- mov di,E820Buf
- xor ax,ax
- mov cx,10
- rep stosw ; Clear buffer
- xor ebx,ebx ; Start with first record
- jmp short .do_e820 ; Skip "at end" check first time!
-.int_loop: and ebx,ebx ; If we're back at beginning...
- jz .e820_done ; ... we're done
-.do_e820: mov eax,0000E820h
- mov edx,534D4150h ; "SMAP" backwards
- xor ecx,ecx
- mov cl,20 ; ECX <- 20 (size of buffer)
- mov di,E820Buf
- int 15h
- jnc .no_carry
- ; If carry, ebx == 0 means error, ebx != 0 means we're done
- and ebx,ebx
- jnz .e820_done
- jmp no_e820
-.no_carry:
- cmp eax,534D4150h
- jne no_e820
- cmp cx,20
- jb no_e820
-
-;
-; Look for a memory block starting at <= 1 MB and continuing upward
-;
- cmp dword [E820Buf+4], byte 0
- ja .int_loop ; Start >= 4 GB?
- mov eax, [E820Buf]
- cmp dword [E820Buf+16],1
- je .is_ram ; Is it memory?
- ;
- ; Non-memory range. Remember this as a limit; some BIOSes get the length
- ; of primary RAM incorrect!
- ;
-.not_ram:
- cmp eax, (1 << 20)
- jb .int_loop ; Starts in lowmem region
- cmp eax,[E820Max]
- jae .int_loop ; Already above limit
- mov [E820Max],eax ; Set limit
- jmp .int_loop
-
-.is_ram:
- cmp eax,[E820Mem]
- ja .int_loop ; Not contiguous with our starting point
- add eax,[E820Buf+8]
- jc .overflow
- cmp dword [E820Buf+12],0
- je .nooverflow
-.overflow:
- or eax,-1
-.nooverflow:
- cmp eax,[E820Mem]
- jbe .int_loop ; All is below our baseline
- mov [E820Mem],eax
- jmp .start_over ; Start over in case we find an adjacent range
-
-.e820_done:
- mov eax,[E820Mem]
- cmp eax,[E820Max]
- jna .not_limited
- mov eax,[E820Max]
-.not_limited:
- cmp eax,(1 << 20)
- ja got_highmem ; Did we actually find memory?
- ; otherwise fall through
-
-;
-; INT 15:E820 failed. Try INT 15:E801.
-;
-no_e820:
- mov ax,0e801h ; Query high memory (semi-recent)
- int 15h
- jc no_e801
- cmp ax,3c00h
- ja no_e801 ; > 3C00h something's wrong with this call
- jb e801_hole ; If memory hole we can only use low part
-
- mov ax,bx
- shl eax,16 ; 64K chunks
- add eax,(16 << 20) ; Add first 16M
- jmp short got_highmem
-
-;
-; INT 15:E801 failed. Try INT 15:88.
-;
-no_e801:
- mov ah,88h ; Query high memory (oldest)
- int 15h
- cmp ax,14*1024 ; Don't trust memory >15M
- jna e801_hole
- mov ax,14*1024
-e801_hole:
- and eax,0ffffh
- shl eax,10 ; Convert from kilobytes
- add eax,(1 << 20) ; First megabyte
-got_highmem:
-%if HIGHMEM_SLOP != 0
- sub eax,HIGHMEM_SLOP
-%endif
- mov [HighMemSize],eax
- popad
- popfd
- pop es
- ret ; Done!
-
- section .bss16
- alignb 4
-E820Buf resd 5 ; INT 15:E820 data buffer
-E820Mem resd 1 ; Memory detected by E820
-E820Max resd 1 ; Is E820 memory capped?
-; HighMemSize is defined in com32.inc
diff --git a/core/idle.c b/core/idle.c
index 3f57393b..c8050554 100644
--- a/core/idle.c
+++ b/core/idle.c
@@ -1,5 +1,5 @@
-/* -*- fundamental -*- ---------------------------------------------------
- *
+/* ----------------------------------------------------------------------- *
+ *
* Copyright 2008 H. Peter Anvin - All Rights Reserved
* Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
*
@@ -24,17 +24,18 @@
#define TICKS_TO_IDLE 4 /* Also in idle.inc */
-extern uint32_t _IdleTimer;
-extern uint16_t NoHalt;
+static jiffies_t _IdleTimer;
+__export uint16_t NoHalt = 0;
int (*idle_hook_func)(void);
void reset_idle(void)
{
_IdleTimer = jiffies();
+ sti(); /* Guard against BIOS/PXE brokenness... */
}
-void __idle(void)
+__export void __idle(void)
{
if (jiffies() - _IdleTimer < TICKS_TO_IDLE)
return;
@@ -42,6 +43,7 @@ void __idle(void)
if (idle_hook_func && idle_hook_func())
return; /* Nonzero return = do not idle */
+ sti();
if (NoHalt)
cpu_relax();
else
diff --git a/core/idle.inc b/core/idle.inc
deleted file mode 100644
index 9677c822..00000000
--- a/core/idle.inc
+++ /dev/null
@@ -1,81 +0,0 @@
-;; -*- fundamental -*- ---------------------------------------------------
-;;
-;; Copyright 2008 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
-;; Boston MA 02110-1301, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
- section .text16
-TICKS_TO_IDLE equ 4 ; Also in idle.c
-
-reset_idle:
- push eax
- mov eax,[cs:__jiffies]
- mov [cs:_IdleTimer],eax
- pop eax
- sti ; Guard against BIOS/PXE brokenness...
- ret
-
-do_idle:
- push eax
- push ds
- push es
- mov ax,cs
- mov ds,ax
- mov es,ax
- pushf
- pop ax
- test ah,2
- jnz .ok
- push si
- push cx
- mov si,hlt_err
- call writestr
- mov si,sp
- add si,10
- mov cx,16
-.errloop:
- ss lodsw
- call writehex4
- dec cx
- jz .endloop
- mov al,' '
- call writechr
- jmp .errloop
-.endloop:
- call crlf
- pop cx
- pop si
- sti
-.ok:
- ; Don't spend time jumping to PM unless we're actually idle...
-
- mov eax,[__jiffies]
- sub eax,[_IdleTimer]
- cmp eax,TICKS_TO_IDLE
- jb .done
-
- extern __idle
- pm_call __idle
-.done:
- pop es
- pop ds
- pop eax
-.ret: ret
-
- section .data16
- alignz 4
- global _IdleTimer
-_IdleTimer dd 0
- global NoHalt
-NoHalt dw 0
-
-hlt_err db 'ERROR: idle with IF=0', CR, LF, 0
-
- section .text16
diff --git a/core/include/bios.h b/core/include/bios.h
new file mode 100644
index 00000000..6c3c8154
--- /dev/null
+++ b/core/include/bios.h
@@ -0,0 +1,114 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ *
+ *
+ * bios.h
+ *
+ * Header file for the BIOS data structures etc.
+ */
+
+#ifndef _BIOS_H
+#define _BIOS_H
+
+#include <sys/io.h>
+
+/*
+ * Interrupt vectors
+ */
+#define BIOS_timer_hook (4 * 0x1C)
+#define fdctab (4 * 0x1E)
+#define fdctab1 fdctab
+#define fdctab2 (fdctab + 2)
+
+#define SERIAL_BASE 0x0400 /* Base address for 4 serial ports */
+#define BIOS_fbm 0x0413 /* Free Base Memory (kilobytes) */
+#define BIOS_page 0x0462 /* Current video page */
+#define BIOS_timer 0x046C /* Timer ticks */
+#define BIOS_magic 0x0472 /* BIOS reset magic */
+#define BIOS_vidrows 0x0484 /* Number of screen rows */
+
+static inline uint16_t bios_fbm(void)
+{
+ return *(volatile uint16_t *)BIOS_fbm;
+}
+
+static inline void set_bios_fbm(uint16_t mem)
+{
+ *(volatile uint16_t *)BIOS_fbm = mem;
+}
+
+#define serial_buf_size 4096
+#define IO_DELAY_PORT 0x80 /* Invalid port (we hope!) */
+
+static inline void io_delay(void)
+{
+ outb(0x0, IO_DELAY_PORT);
+ outb(0x0, IO_DELAY_PORT);
+}
+
+/* conio.c */
+extern unsigned short SerialPort;
+extern unsigned char FlowIgnore;
+extern uint8_t ScrollAttribute;
+extern uint16_t DisplayCon;
+
+/*
+ * Sometimes we need to access screen coordinates as separate 8-bit
+ * entities and sometimes we need to use them as 16-bit entities. Using
+ * this structure allows the compiler to do it for us.
+ */
+union screen {
+ struct {
+ uint8_t col; /* Cursor column for message file */
+ uint8_t row; /* Cursor row for message file */
+ } b;
+ uint16_t dx;
+};
+extern union screen _cursor;
+extern union screen _screensize;
+
+#define CursorDX _cursor.dx
+#define CursorCol _cursor.b.col
+#define CursorRow _cursor.b.row
+
+#define ScreenSize _screensize.dx
+#define VidCols _screensize.b.col
+#define VidRows _screensize.b.row
+
+/* font.c */
+extern void use_font(void);
+extern void bios_adjust_screen(void);
+
+/* serirq.c */
+extern char *SerialHead;
+extern char *SerialTail;
+
+extern void bios_init(void);
+extern void bios_cleanup_hardware(void);
+
+static inline uint16_t get_serial_port(uint16_t port)
+{
+ /* Magic array in BIOS memory, contains four entries */
+ const uint16_t * const serial_ports = (const uint16_t *)SERIAL_BASE;
+
+ /*
+ * If port > 3 then the port is simply the I/O base address
+ */
+ if (port > 3)
+ return port;
+
+ /* Get the I/O port from the BIOS */
+ return serial_ports[port];
+}
+
+#endif /* _BIOS_H */
diff --git a/core/include/core.h b/core/include/core.h
index 114b049a..d35bd038 100644
--- a/core/include/core.h
+++ b/core/include/core.h
@@ -2,8 +2,18 @@
#define CORE_H
#include <klibc/compiler.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <dprintf.h>
#include <com32.h>
+#include <errno.h>
#include <syslinux/pmapi.h>
+#include <syslinux/sysappend.h>
+#include <kaboom.h>
+#include <timer.h>
extern char core_xfer_buf[65536];
extern char core_cache_buf[65536];
@@ -11,13 +21,34 @@ extern char trackbuf[];
extern char CurrentDirName[];
extern char SubvolName[];
extern char ConfigName[];
+extern char config_cwd[];
extern char KernelName[];
extern char cmd_line[];
extern char ConfigFile[];
+extern char syslinux_banner[];
+extern char copyright_str[];
+extern uint16_t BIOSName;
+extern char StackBuf[];
+extern unsigned int __bcopyxx_len;
+
+extern uint8_t KbdMap[256];
+
+extern const uint16_t IPAppends[];
+extern const char numIPAppends[];
+
+extern uint16_t SerialPort;
+extern uint16_t BaudDivisor;
+extern uint8_t FlowOutput;
+extern uint8_t FlowInput;
+extern uint8_t FlowIgnore;
/* diskstart.inc isolinux.asm*/
extern void getlinsec(void);
+/* pm.inc */
+void core_pm_null_hook(void);
+extern void (*core_pm_hook)(void);
+
/* getc.inc */
extern void core_open(void);
@@ -30,13 +61,18 @@ extern void __idle(void);
extern void reset_idle(void);
/* mem/malloc.c, mem/free.c, mem/init.c */
-extern void *malloc(size_t);
extern void *lmalloc(size_t);
extern void *pmapi_lmalloc(size_t);
extern void *zalloc(size_t);
extern void free(void *);
extern void mem_init(void);
+/* sysappend.c */
+extern void print_sysappend(void);
+extern const char *sysappend_strings[SYSAPPEND_MAX];
+extern uint32_t SysAppends;
+extern void sysappend_set_uuid(const uint8_t *uuid);
+
void __cdecl core_intcall(uint8_t, const com32sys_t *, com32sys_t *);
void __cdecl core_farcall(uint32_t, const com32sys_t *, com32sys_t *);
int __cdecl core_cfarcall(uint32_t, const void *, uint32_t);
@@ -51,31 +87,6 @@ void call16(void (*)(void), const com32sys_t *, com32sys_t *);
#define __bss16 __attribute__((nocommon,section(".bss16")))
/*
- * Section for very large aligned objects, not zeroed on startup
- */
-#define __hugebss __attribute__((nocommon,section(".hugebss"),aligned(4096)))
-
-/*
- * Death! The macro trick is to avoid symbol conflict with
- * the real-mode symbol kaboom.
- */
-__noreturn _kaboom(void);
-#define kaboom() _kaboom()
-
-/*
- * Basic timer function...
- */
-extern volatile uint32_t __jiffies, __ms_timer;
-static inline uint32_t jiffies(void)
-{
- return __jiffies;
-}
-static inline uint32_t ms_timer(void)
-{
- return __ms_timer;
-}
-
-/*
* Helper routine to return a specific set of flags
*/
static inline void set_flags(com32sys_t *regs, uint32_t flags)
@@ -88,4 +99,25 @@ static inline void set_flags(com32sys_t *regs, uint32_t flags)
regs->eflags.l = eflags;
}
+extern int start_ldlinux(int argc, char **argv);
+extern int create_args_and_load(char *);
+
+extern void write_serial(char data);
+extern void writestr(char *str);
+extern void writechr(char data);
+extern void crlf(void);
+extern int pollchar(void);
+extern char getchar(char *hi);
+
+extern void cleanup_hardware(void);
+extern void sirq_cleanup(void);
+extern void adjust_screen(void);
+
+extern void execute(const char *cmdline, uint32_t type, bool sysappend);
+extern void load_kernel(const char *cmdline);
+
+extern void dmi_init(void);
+
+extern void do_sysappend(char *buf);
+
#endif /* CORE_H */
diff --git a/core/include/ctype.h b/core/include/ctype.h
index 5c6d4cb4..6c7f57f4 100644
--- a/core/include/ctype.h
+++ b/core/include/ctype.h
@@ -22,4 +22,17 @@ static inline int tolower(int c)
return c;
}
+static inline int isspace(int ch)
+{
+ int space = 0;
+ if ((ch == ' ') ||
+ (ch == '\f') ||
+ (ch == '\n') ||
+ (ch == '\r') ||
+ (ch == '\t') ||
+ (ch == '\v'))
+ space = 1;
+ return space;
+}
+
#endif /* CTYPE_H */
diff --git a/core/include/fs.h b/core/include/fs.h
index e1f5733c..b5c7f0d9 100644
--- a/core/include/fs.h
+++ b/core/include/fs.h
@@ -1,22 +1,21 @@
#ifndef FS_H
#define FS_H
+#include <linux/list.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <com32.h>
#include <stdio.h>
#include <sys/dirent.h>
+#include <dprintf.h>
#include "core.h"
#include "disk.h"
/*
- * Maximum number of open files. This is *currently* constrained by the
- * fact that PXE needs to be able to fit all its packet buffers into a
- * 64K segment; this should be fixed by moving the packet buffers to high
- * memory.
+ * Maximum number of open files.
*/
-#define MAX_OPEN_LG2 5
+#define MAX_OPEN_LG2 7
#define MAX_OPEN (1 << MAX_OPEN_LG2)
#define FILENAME_MAX_LG2 8
@@ -55,13 +54,14 @@ struct fs_ops {
enum fs_flags fs_flags;
int (*fs_init)(struct fs_info *);
- void (*searchdir)(const char *, struct file *);
+ void (*searchdir)(const char *, int, struct file *);
uint32_t (*getfssec)(struct file *, char *, int, bool *);
void (*close_file)(struct file *);
void (*mangle_name)(char *, const char *);
size_t (*realpath)(struct fs_info *, char *, const char *, size_t);
int (*chdir)(struct fs_info *, const char *);
- int (*load_config)(void);
+ int (*chdir_start)(void);
+ int (*open_config)(struct com32_filedata *);
struct inode * (*iget_root)(struct fs_info *);
struct inode * (*iget)(const char *, struct inode *);
@@ -71,6 +71,8 @@ struct fs_ops {
int (*readdir)(struct file *, struct dirent *);
int (*next_extent)(struct inode *, uint32_t);
+
+ int (*copy_super)(void *buf);
};
/*
@@ -98,9 +100,9 @@ struct inode {
const char *name; /* Name, valid for generic path search only */
int refcnt;
int mode; /* FILE , DIR or SYMLINK */
- uint32_t size;
- uint32_t blocks; /* How many blocks the file take */
- uint32_t ino; /* Inode number */
+ uint64_t size;
+ uint64_t blocks; /* How many blocks the file take */
+ uint64_t ino; /* Inode number */
uint32_t atime; /* Access time */
uint32_t mtime; /* Modify time */
uint32_t ctime; /* Create time */
@@ -155,6 +157,8 @@ static inline void free_inode(struct inode * inode)
static inline struct inode *get_inode(struct inode *inode)
{
inode->refcnt++;
+ dprintf("get_inode %p name %s refcnt %d\n",
+ inode, inode->name, inode->refcnt);
return inode;
}
@@ -179,17 +183,27 @@ static inline struct file *handle_to_file(uint16_t handle)
return handle ? &files[handle-1] : NULL;
}
+struct path_entry {
+ struct list_head list;
+ const char *str;
+};
+
+extern struct list_head PATH;
+
+extern struct path_entry *path_add(const char *str);
+
/* fs.c */
void pm_mangle_name(com32sys_t *);
void pm_searchdir(com32sys_t *);
void mangle_name(char *, const char *);
-int searchdir(const char *name);
+int searchdir(const char *name, int flags);
void _close_file(struct file *);
size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors);
-int open_file(const char *name, struct com32_filedata *filedata);
+int open_file(const char *name, int flags, struct com32_filedata *filedata);
void pm_open_file(com32sys_t *);
void close_file(uint16_t handle);
void pm_close_file(com32sys_t *);
+int open_config(void);
/* chdir.c */
void pm_realpath(com32sys_t *regs);
@@ -202,18 +216,23 @@ struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
/* getcwd.c */
-char *getcwd(char *buf, size_t size);
+char *core_getcwd(char *buf, size_t size);
/*
* Generic functions that filesystem drivers may choose to use
*/
+/* chdir.c */
+int generic_chdir_start(void);
+
/* mangle.c */
void generic_mangle_name(char *, const char *);
/* loadconfig.c */
-int search_config(const char *search_directores[], const char *filenames[]);
-int generic_load_config(void);
+int search_dirs(struct com32_filedata *filedata,
+ const char *search_directores[], const char *filenames[],
+ char *realname);
+int generic_open_config(struct com32_filedata *filedata);
/* close.c */
void generic_close_file(struct file *file);
diff --git a/com32/lib/syslinux/features.c b/core/include/graphics.h
index c88aef30..814ffe7d 100644
--- a/com32/lib/syslinux/features.c
+++ b/core/include/graphics.h
@@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
- * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2012 Paulo Alcantara <pcacjr@zytor.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -25,27 +25,39 @@
*
* ----------------------------------------------------------------------- */
-/*
- * syslinux/features.c
- *
- * SYSLINUX feature flag query
- */
+#ifndef GRAPHICS_H_
+#define GRAPHICS_H_
-#include <klibc/compiler.h>
-#include <syslinux/features.h>
-#include <string.h>
-#include <com32.h>
+#include <stddef.h>
-struct __syslinux_feature_flags __syslinux_feature_flags;
+#include "core.h"
+#include "fs.h"
-void __constructor __syslinux_detect_features(void)
-{
- static com32sys_t reg;
+#ifdef IS_SYSLINUX
+#define VGA_FILE_BUF_SIZE (FILENAME_MAX + 2)
+#else
+#define VGA_FILE_BUF_SIZE FILENAME_MAX
+#endif
+
+extern uint8_t UsingVGA;
+extern uint16_t VGAPos;
+extern uint16_t *VGAFilePtr;
+extern char VGAFileBuf[VGA_FILE_BUF_SIZE];
+extern char VGAFileMBuf[];
+extern uint16_t VGAFontSize;
+
+extern uint8_t UserFont;
- memset(&reg, 0, sizeof reg);
- reg.eax.w[0] = 0x0015;
- __intcall(0x22, &reg, &reg);
+extern __lowmem char fontbuf[8192];
- __syslinux_feature_flags.len = reg.ecx.w[0];
- __syslinux_feature_flags.ptr = MK_PTR(reg.es, reg.ebx.w[0]);
+extern void syslinux_force_text_mode(void);
+extern void vgadisplayfile(FILE *_fd);
+extern void using_vga(uint8_t vga, uint16_t pix_cols, uint16_t pix_rows);
+
+static inline void graphics_using_vga(uint8_t vga, uint16_t pix_cols,
+ uint16_t pix_rows)
+{
+ using_vga(vga, pix_cols, pix_rows);
}
+
+#endif /* GRAPHICS_H_ */
diff --git a/core/include/kaboom.h b/core/include/kaboom.h
new file mode 100644
index 00000000..4a763be9
--- /dev/null
+++ b/core/include/kaboom.h
@@ -0,0 +1,11 @@
+#ifndef KABOOM_H
+#define KABOOM_H
+
+/*
+ * Death! The macro trick is to avoid symbol conflict with
+ * the real-mode symbol kaboom.
+ */
+__noreturn _kaboom(void);
+#define kaboom() _kaboom()
+
+#endif /* KABOOM_H */
diff --git a/com32/lib/sys/times.c b/core/include/localboot.h
index dd063f37..4bb79a6a 100644
--- a/com32/lib/sys/times.c
+++ b/core/include/localboot.h
@@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
- * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2012 Paulo Alcantara <pcacjr@zytor.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -25,18 +25,9 @@
*
* ----------------------------------------------------------------------- */
-/*
- * sys/times.c
- *
- * Returns something like a clock.
- */
+#ifndef LOCALBOOT_H_
+#define LOCALBOOT_H_
-#include <sys/times.h>
-#include <syslinux/pmapi.h>
-#include <com32.h>
+extern void local_boot(int16_t ax);
-clock_t times(struct tms * buf)
-{
- (void)buf;
- return *__com32.cs_pm->ms_timer;
-}
+#endif /* LOCALBOOT_H_ */
diff --git a/core/include/mbox.h b/core/include/mbox.h
new file mode 100644
index 00000000..6fec267c
--- /dev/null
+++ b/core/include/mbox.h
@@ -0,0 +1,60 @@
+/*
+ * mbox.h
+ *
+ * Simple thread mailbox interface
+ */
+
+#ifndef _MBOX_H
+#define _MBOX_H
+
+#include "thread.h"
+
+/*
+ * If a mailbox is allocated statically (as a struct mailbox), this
+ * is the number of slots it gets.
+ */
+#define MAILBOX_STATIC_SIZE 512
+
+struct mailbox {
+ struct semaphore prod_sem; /* Producer semaphore (empty slots) */
+ struct semaphore cons_sem; /* Consumer semaphore (data slots) */
+ struct semaphore head_sem; /* Head pointer semaphore */
+ struct semaphore tail_sem; /* Tail pointer semaphore */
+ void **wrap; /* Where pointers wrap */
+ void **head; /* Head pointer */
+ void **tail; /* Tail pointer */
+
+ void *data[MAILBOX_STATIC_SIZE]; /* Data array */
+};
+
+/* The number of bytes for an mailbox of size s */
+#define MBOX_BYTES(s) (sizeof(struct mailbox) + \
+ ((s)-MAILBOX_STATIC_SIZE)*sizeof(void *))
+
+void mbox_init(struct mailbox *mbox, size_t size);
+int mbox_post(struct mailbox *mbox, void *msg, mstime_t timeout);
+mstime_t mbox_fetch(struct mailbox *mbox, void **msg, mstime_t timeout);
+
+/*
+ * This marks a mailbox object as unusable; it will remain unusable
+ * until sem_init() is called on it again. This DOES NOT clear the
+ * list of blocked processes on this mailbox!
+ *
+ * It is also possible to mark the mailbox invalid by zeroing its
+ * memory structure.
+ */
+static inline void mbox_set_invalid(struct mailbox *mbox)
+{
+ if (!!mbox)
+ sem_set_invalid(&mbox->prod_sem);
+}
+
+/*
+ * Ask if a mailbox object has been initialized.
+ */
+static inline bool mbox_is_valid(struct mailbox *mbox)
+{
+ return ((!!mbox) && sem_is_valid(&mbox->prod_sem));
+}
+
+#endif /* _MBOX_H */
diff --git a/core/include/net.h b/core/include/net.h
new file mode 100644
index 00000000..f970ab0f
--- /dev/null
+++ b/core/include/net.h
@@ -0,0 +1,36 @@
+#ifndef _NET_H
+#define _NET_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Protocol family */
+enum net_core_proto {
+ NET_CORE_TCP,
+ NET_CORE_UDP,
+};
+
+void net_core_init(void);
+
+struct pxe_pvt_inode;
+
+int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto);
+void net_core_close(struct pxe_pvt_inode *socket);
+
+void net_core_connect(struct pxe_pvt_inode *socket,
+ uint32_t ip, uint16_t port);
+void net_core_disconnect(struct pxe_pvt_inode *socket);
+
+int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+ uint32_t *src_ip, uint16_t *src_port);
+
+void net_core_send(struct pxe_pvt_inode *socket,
+ const void *data, size_t len);
+
+void net_core_sendto(struct pxe_pvt_inode *socket, const void *data, size_t len,
+ uint32_t ip, uint16_t port);
+
+void probe_undi(void);
+void pxe_init_isr(void);
+
+#endif /* _NET_H */
diff --git a/core/include/thread.h b/core/include/thread.h
new file mode 100644
index 00000000..8ec4a267
--- /dev/null
+++ b/core/include/thread.h
@@ -0,0 +1,116 @@
+#ifndef _THREAD_H
+#define _THREAD_H
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <timer.h>
+#include <sys/cpu.h>
+
+/* The idle thread runs at this priority */
+#define IDLE_THREAD_PRIORITY INT_MAX
+
+/* This priority should normally be used for hardware-polling threads */
+#define POLL_THREAD_PRIORITY (INT_MAX-1)
+
+struct semaphore;
+
+struct thread_list {
+ struct thread_list *next, *prev;
+};
+
+/*
+ * Stack frame used by __switch_to, see thread_asm.S
+ */
+struct thread_stack {
+ int errno;
+ uint16_t rmsp, rmss;
+ uint32_t edi, esi, ebp, ebx;
+ void (*eip)(void);
+};
+
+struct thread_block {
+ struct thread_list list;
+ struct thread *thread;
+ struct semaphore *semaphore;
+ mstime_t block_time;
+ mstime_t timeout;
+ bool timed_out;
+};
+
+#define THREAD_MAGIC 0x3568eb7d
+
+struct thread {
+ struct thread_stack *esp; /* Must be first; stack pointer */
+ unsigned int thread_magic;
+ const char *name; /* Name (for debugging) */
+ struct thread_list list;
+ struct thread_block *blocked;
+ void *stack, *rmstack; /* Stacks, iff allocated by malloc/lmalloc */
+ void *pvt; /* For the benefit of lwIP */
+ int prio;
+};
+
+extern void (*sched_hook_func)(void);
+
+void __thread_process_timeouts(void);
+void __schedule(void);
+void __switch_to(struct thread *);
+void thread_yield(void);
+
+extern struct thread *__current;
+static inline struct thread *current(void)
+{
+ return __current;
+}
+
+struct semaphore {
+ int count;
+ struct thread_list list;
+};
+
+#define DECLARE_INIT_SEMAPHORE(sem, cnt) \
+ struct semaphore sem = { \
+ .count = (cnt), \
+ .list = { \
+ .next = &sem.list, \
+ .prev = &sem.list \
+ } \
+ }
+
+mstime_t sem_down(struct semaphore *, mstime_t);
+void sem_up(struct semaphore *);
+void sem_init(struct semaphore *, int);
+
+/*
+ * This marks a semaphore object as unusable; it will remain unusable
+ * until sem_init() is called on it again. This DOES NOT clear the
+ * list of blocked processes on this semaphore!
+ *
+ * It is also possible to mark the semaphore invalid by zeroing its
+ * memory structure.
+ */
+static inline void sem_set_invalid(struct semaphore *sem)
+{
+ if (!!sem)
+ sem->list.next = NULL;
+}
+
+/*
+ * Ask if a semaphore object has been initialized.
+ */
+static inline bool sem_is_valid(struct semaphore *sem)
+{
+ return ((!!sem) && (!!sem->list.next));
+}
+
+struct thread *start_thread(const char *name, size_t stack_size, int prio,
+ void (*start_func)(void *), void *func_arg);
+void __exit_thread(void);
+void kill_thread(struct thread *);
+
+void start_idle_thread(void);
+void test_thread(void);
+
+#endif /* _THREAD_H */
diff --git a/core/include/timer.h b/core/include/timer.h
new file mode 100644
index 00000000..1d66ba73
--- /dev/null
+++ b/core/include/timer.h
@@ -0,0 +1,21 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+/*
+ * Basic timer function...
+ */
+typedef uint32_t jiffies_t;
+extern volatile jiffies_t __jiffies, __ms_timer;
+static inline jiffies_t jiffies(void)
+{
+ return __jiffies;
+}
+
+typedef uint32_t mstime_t;
+typedef int32_t mstimediff_t;
+static inline mstime_t ms_timer(void)
+{
+ return __ms_timer;
+}
+
+#endif /* TIMER_H */
diff --git a/core/init.c b/core/init.c
new file mode 100644
index 00000000..f286622f
--- /dev/null
+++ b/core/init.c
@@ -0,0 +1,92 @@
+#include <core.h>
+#include <com32.h>
+#include <sys/io.h>
+#include <fs.h>
+#include <bios.h>
+
+static uint32_t min_lowmem_heap = 65536;
+extern char __lowmem_heap[];
+uint8_t KbdFlags; /* Check for keyboard escapes */
+__export uint8_t KbdMap[256]; /* Keyboard map */
+
+__export uint16_t PXERetry;
+
+static inline void check_escapes(void)
+{
+ com32sys_t ireg, oreg;
+
+ ireg.eax.b[1] = 0x02; /* Check keyboard flags */
+ __intcall(0x16, &ireg, &oreg);
+
+ KbdFlags = oreg.eax.b[0];
+
+ /* Ctrl->skip 386 check */
+ if (oreg.eax.b[0] & 0x04) {
+ /*
+ * Now check that there is sufficient low (DOS) memory
+ *
+ * NOTE: Linux doesn't use all of real_mode_seg, but we use
+ * the same segment for COMBOOT images, which can use all 64K.
+ */
+ uint16_t mem;
+
+ __intcall(0x12, &ireg, &oreg);
+
+ mem = ((uint32_t)__lowmem_heap) + min_lowmem_heap + 1023;
+ mem = mem >> 10;
+
+ if (mem < oreg.eax.w[0]) {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf),
+ "It appears your computer has only "
+ "%dK of low (\"DOS\") RAM.\n"
+ "This version of Syslinux needs "
+ "%dK to boot. "
+ "If you get this\nmessage in error, "
+ "hold down the Ctrl key while booting, "
+ "and I\nwill take your word for it.\n",
+ oreg.eax.w[0], mem);
+ writestr(buf);
+ kaboom();
+ }
+ }
+}
+
+extern uint32_t BIOS_timer_next;
+extern uint32_t timer_irq;
+static inline void bios_timer_init(void)
+{
+ unsigned long next;
+ uint32_t *hook = (uint32_t *)BIOS_timer_hook;
+
+ next = *hook;
+ BIOS_timer_next = next;
+ *hook = (uint32_t)&timer_irq;
+}
+
+void init(com32sys_t *regs __unused)
+{
+ int i;
+
+ /* Initialize timer */
+ bios_timer_init();
+
+ for (i = 0; i < 256; i++)
+ KbdMap[i] = i;
+
+ adjust_screen();
+
+ /* Init the memory subsystem */
+ mem_init();
+
+ dprintf("%s%s", syslinux_banner, copyright_str);
+
+ /* CPU-dependent initialization and related checks. */
+ check_escapes();
+
+ /*
+ * Scan the DMI tables for interesting information.
+ */
+ dmi_init();
+}
diff --git a/core/init.inc b/core/init.inc
index 8c6a178f..ae0e6312 100644
--- a/core/init.inc
+++ b/core/init.inc
@@ -23,97 +23,39 @@ common_init:
; Initialize PM invocation framework
call pm_init
+%if IS_PXELINUX
+ ; Save derivative-specific data
+ pm_call pm_save_data
+%endif
+
; Decompress PM code to its target location
pm_call pm_decompress
cmp eax,__pm_code_len
jne kaboom
-;
-; Initialize timer
-;
- call timer_init
-
-;
-; Initialize configuration information
-;
- call reset_config
+ extern init
+ pm_call init
;
; Set up the COMBOOT APIs
;
call comboot_setup_api
-;
-; Now set up screen parameters
-;
- call adjust_screen
-
-;
-; CPU-dependent initialization and related checks.
-;
-check_escapes:
- mov ah,02h ; Check keyboard flags
- int 16h
- mov [KbdFlags],al ; Save for boot prompt check
- test al,04h ; Ctrl->skip 386 check
- jnz skip_checks
-
-;
-; Now check that there is sufficient low (DOS) memory
-;
-; NOTE: Linux doesn't use all of real_mode_seg, but we use the same
-; segment for COMBOOT images, which can use all 64K
-;
- int 12h
- mov edx,__lowmem_heap + min_lowmem_heap + 1023
- shr edx,10
- cmp ax,dx
- jae enough_ram
- mov si,err_noram
- mov cl,10
- push dx
- div cl
- add [si+err_noram.need-err_noram+2],ah
- cbw
- div cl
- add [si+err_noram.need-err_noram],ax
- pop ax
- div cl
- add [si+err_noram.size-err_noram+2],ah
- cbw
- div cl
- add [si+err_noram.size-err_noram],ax
- call writestr_early
- jmp kaboom
-enough_ram:
-skip_checks:
-
- section .data16
-err_noram db 'It appears your computer has only '
-.size db '000'
- db 'K of low ("DOS") RAM.', CR, LF
- db 'This version of Syslinux needs '
-.need db '000'
- db 'K to boot. If you get this', CR, LF
- db 'message in error, hold down the Ctrl key while'
- db 'booting, and I', CR, LF
- db 'will take your word for it.', CR, LF, 0
-
section .text16
;
; The code to decompress the PM code and initialize other segments.
;
- extern _lzo1x_decompress_asm_fast
+ extern _lzo1x_decompress_asm_fast_safe
section .textnr
bits 32
pm_decompress:
- push 0 ; Space for decompressed size
+ push __pm_code_len + 16 ; Space for decompressed size
push esp ; Pointer to previous word
push __pm_code_start ; Target address
push dword [lzo_data_size] ; Compressed size
push dword __pm_code_lma
- call _lzo1x_decompress_asm_fast
+ call _lzo1x_decompress_asm_fast_safe
add esp,16
pop RM_EAX ; Decompressed size
@@ -126,7 +68,7 @@ pm_decompress:
mov edi,__bss16_start
mov ecx,__bss16_dwords
rep stosd
- mov edi,__high_clear_start ; .uibss, .auxseg, .lowmem
+ mov edi,__high_clear_start ; .uibss, .lowmem
mov ecx,__high_clear_dwords
rep stosd
diff --git a/core/isolinux.asm b/core/isolinux.asm
index dd0fa892..4dc8be6d 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -35,23 +35,6 @@ SECTOR_SIZE equ (1 << SECTOR_SHIFT)
ROOT_DIR_WORD equ 0x002F
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_appendlen: resw 1
-vk_type: resb 1 ; Type of file
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -68,6 +51,7 @@ trackbuf resb trackbufsize ; Track buffer goes here
; Some of these are touched before the whole image
; is loaded. DO NOT move this to .bss16/.uibss.
section .earlybss
+ global BIOSName
alignb 4
FirstSecSum resd 1 ; Checksum of bytes 64-2048
ImageDwords resd 1 ; isolinux.bin size, dwords
@@ -167,6 +151,7 @@ _spec_len equ _spec_end - _spec_start
;; CD-ROM sector (2K) of the file, so the number one priority is actually
;; loading the rest.
;;
+ global StackBuf
StackBuf equ STACK_TOP-44 ; 44 bytes needed for
; the bootsector chainloading
; code!
@@ -286,7 +271,7 @@ initial_csum: xor edi,edi
call writemsg
mov al,dl
call writehex2
- call crlf
+ call crlf_early
%endif
;
; Initialize spec packet buffers
@@ -326,7 +311,7 @@ initial_csum: xor edi,edi
call writemsg
mov al,byte [sp_drive]
call writehex2
- call crlf
+ call crlf_early
%endif
found_drive:
@@ -393,7 +378,7 @@ found_file:
mov si,offset_msg
call writemsg
call writehex8
- call crlf
+ call crlf_early
%endif
; Load the rest of the file. However, just in case there
@@ -524,7 +509,7 @@ award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
award_hack: mov si,spec_err_msg ; Moved to this place from
- call writemsg ; spec_query_faild
+ call writemsg ; spec_query_failed
;
%ifdef DEBUG_MESSAGES ;
;
@@ -626,7 +611,7 @@ spec_query_failed:
call writemsg
mov al,dl
call writehex2
- call crlf
+ call crlf_early
cmp byte [sp_drive],dl
jne .maybe_broken
@@ -670,7 +655,7 @@ spec_query_failed:
call writemsg
mov al,dl
call writehex2
- call crlf
+ call crlf_early
mov si,trysbm_msg
call writemsg
jmp .found_drive ; Pray that this works...
@@ -693,6 +678,26 @@ writemsg: push ax
pop ax
ret
+writestr_early:
+ pushfd
+ pushad
+.top: lodsb
+ and al,al
+ jz .end
+ call writechr
+ jmp short .top
+.end: popad
+ popfd
+ ret
+
+crlf_early: push ax
+ mov al,CR
+ call writechr
+ mov al,LF
+ call writechr
+ pop ax
+ ret
+
;
; Write a character to the screen. There is a more "sophisticated"
; version of this in the subsequent code, so we patch the pointer
@@ -1007,7 +1012,7 @@ xint13: mov byte [RetryCount],retry_count
call writestr_early
mov al,dl
call writehex2
- call crlf
+ call crlf_early
; Fall through to kaboom
;
@@ -1018,9 +1023,9 @@ xint13: mov byte [RetryCount],retry_count
disk_error:
kaboom:
RESET_STACK_AND_SEGS AX
- mov si,err_bootfailed
- call writestr
- call getchar
+ mov si,bailmsg
+ pm_call pm_writestr
+ pm_call pm_getchar
cli
mov word [BIOS_magic],0 ; Cold reboot
jmp 0F000h:0FFF0h ; Reset vector address
@@ -1029,14 +1034,13 @@ kaboom:
; Common modules needed in the first sector
; -----------------------------------------------------------------------------
-%include "writestr.inc" ; String output
-writestr_early equ writestr
%include "writehex.inc" ; Hexadecimal output
; -----------------------------------------------------------------------------
; Data that needs to be in the first sector
; -----------------------------------------------------------------------------
+ global syslinux_banner, copyright_str
syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0
copyright_str db ' Copyright (C) 1994-'
asciidec YEAR
@@ -1108,18 +1112,10 @@ all_read:
;
%include "init.inc"
- ; Patch the writechr routine to point to the full code
- mov di,writechr
- mov al,0e9h
- stosb
- mov ax,writechr_full-2
- sub ax,di
- stosw
-
; Tell the user we got this far...
%ifndef DEBUG_MESSAGES ; Gets messy with debugging on
mov si,copyright_str
- call writestr_early
+ pm_call pm_writestr
%endif
;
@@ -1159,6 +1155,11 @@ init_fs:
mov si,[bsHeads]
mov di,[bsSecPerTrack]
pm_call fs_init
+ pm_call load_env32
+enter_command:
+auto_boot:
+ jmp kaboom ; load_env32() should never return. If
+ ; it does, then kaboom!
popad
section .rodata
@@ -1170,25 +1171,58 @@ ROOT_FS_OPS:
section .text16
+%ifdef DEBUG_TRACERS
+;
+; debug hack to print a character with minimal code impact
+;
+debug_tracer: pushad
+ pushfd
+ mov bp,sp
+ mov bx,[bp+9*4] ; Get return address
+ mov al,[cs:bx] ; Get data byte
+ inc word [bp+9*4] ; Return to after data byte
+ call writechr
+ popfd
+ popad
+ ret
+%endif ; DEBUG_TRACERS
+
+ section .bss16
+ alignb 4
+ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
+ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
+KernelExtPtr resw 1 ; During search, final null pointer
+FuncFlag resb 1 ; Escape sequences received from keyboard
+KernelType resb 1 ; Kernel type, from vkernel, if known
+ global KernelName
+KernelName resb FILENAME_MAX ; Mangled name for kernel
+
+ section .text16
+;
+; COMBOOT-loading code
+;
+%include "comboot.inc"
+%include "com32.inc"
+
+;
+; Boot sector loading code
+;
+
;
-; Locate the configuration file
+; Abort loading code
;
- pm_call pm_load_config
- jz no_config_file
;
-; Now we have the config file open. Parse the config file and
-; run the user interface.
+; Hardware cleanup common code
;
-%include "ui.inc"
+
+%include "localboot.inc"
; -----------------------------------------------------------------------------
; Common modules
; -----------------------------------------------------------------------------
%include "common.inc" ; Universal modules
-%include "rawcon.inc" ; Console I/O w/o using the console functions
-%include "localboot.inc" ; Disk-based local boot
; -----------------------------------------------------------------------------
; Begin data section
@@ -1196,19 +1230,3 @@ ROOT_FS_OPS:
section .data16
err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
-
-;
-; Config file keyword table
-;
-%include "keywords.inc"
-
-;
-; Extensions to search for (in *forward* order).
-;
- alignz 4
-exten_table: db '.cbt' ; COMBOOT (specific)
- db '.bin' ; CD boot sector
- db '.com' ; COMBOOT (same as DOS)
- db '.c32' ; COM32
-exten_table_end:
- dd 0, 0 ; Need 8 null bytes here
diff --git a/core/kaboom.c b/core/kaboom.c
index d639915a..1686eedc 100644
--- a/core/kaboom.c
+++ b/core/kaboom.c
@@ -4,9 +4,21 @@
#include "core.h"
+#if defined(DEBUG) || defined(DEBUG_PORT)
+
+#include <dprintf.h>
+
+__export __noreturn __bad_SEG(const volatile void *p)
+{
+ dprintf("SEG() passed an invalid pointer: %p\n", p);
+ kaboom();
+}
+
+#endif
+
#undef kaboom
-__noreturn _kaboom(void)
+__export __noreturn _kaboom(void)
{
extern void kaboom(void);
call16(kaboom, &zero_regs, NULL);
diff --git a/core/kernel.inc b/core/kernel.inc
index 245cd6db..5e1c7a39 100644
--- a/core/kernel.inc
+++ b/core/kernel.inc
@@ -62,9 +62,6 @@ linux_fdctab resb 12
cmd_line_here equ $ ; F800 Should be out of the way
endstruc
- global cmd_line
-cmd_line equ core_real_mode + cmd_line_here
-
;
; Old kernel command line signature
;
diff --git a/core/keywords b/core/keywords
index 7f585b48..8af0095f 100644
--- a/core/keywords
+++ b/core/keywords
@@ -34,6 +34,8 @@ onerror
noescape
nocomplete
nohalt
+sysappend
+sendcookies
f0
f1
f2
diff --git a/core/keywords.inc b/core/keywords.inc
index 08d77c64..d91ca4ff 100644
--- a/core/keywords.inc
+++ b/core/keywords.inc
@@ -92,9 +92,11 @@ keywd_table:
keyword f0, pc_filename, FKeyN(10)
keyword f11, pc_filename, FKeyN(11)
keyword f12, pc_filename, FKeyN(12)
+ keyword ipappend, pc_sysappend
+ keyword sysappend, pc_sysappend
+ keyword localboot, pc_localboot
%if IS_PXELINUX
- keyword ipappend, pc_ipappend
+ keyword sendcookies, pc_sendcookies
%endif
- keyword localboot, pc_localboot
keywd_count equ ($-keywd_table)/keywd_size
diff --git a/core/layout.inc b/core/layout.inc
index dab27dde..53ca783d 100644
--- a/core/layout.inc
+++ b/core/layout.inc
@@ -52,7 +52,6 @@ LATEBSS_START equ 0B800h
;
; 32-bit stack layout
;
- global STACK32_LEN
STACK32_LEN equ 64*1024
section .stack nobits write align=4096
@@ -66,6 +65,7 @@ STACK32_LEN equ 64*1024
section .config write progbits align=4
section .replacestub exec write progbits align=16
section .gentextnr exec write nobits align=16
+ section .stack16 write nobits align=16
; Use .bss16 for things that doesn't have to be in low memory;
; .earlybss should be used for things that absolutely have
@@ -97,6 +97,8 @@ RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet...
; the spillover from the last fractional sector load.
section .uibss write nobits align=16
+ section .savedata write nobits align=16
+
; Symbols from linker script
%macro SECINFO 1
extern __%1_start, __%1_lma, __%1_end
@@ -128,27 +130,12 @@ RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet...
serial_buf_size equ 4096 ; Should be a power of 2
;
-; Contents of aux_seg
-;
- extern aux_seg ; Actual segment assigned by linker
-
- struc aux
-.fontbuf resb 8192
-.serial resb serial_buf_size
-
- alignb 4096 ; Align the next segment to 4K
- endstruc
-
- section .auxseg write nobits align=16
-auxseg resb aux_size
-
-;
; Transfer buffer segment: guaranteed to be aligned 64K, used for disk I/O
; One symbol for the segment number, one for the absolute address
;
extern xfer_buf_seg
section .xfer_buf write nobits align=65536
- global core_xfer_buf
+ global core_xfer_buf:data hidden
core_xfer_buf resb 65536
;
@@ -158,7 +145,7 @@ core_xfer_buf resb 65536
;
extern real_mode_seg
section .real_mode write nobits align=65536
- global core_real_mode
+ global core_real_mode:data hidden
core_real_mode resb 65536
comboot_seg equ real_mode_seg ; COMBOOT image loading zone
diff --git a/core/ldlinux.asm b/core/ldlinux.asm
index a2f859d0..a1f96b77 100644
--- a/core/ldlinux.asm
+++ b/core/ldlinux.asm
@@ -39,6 +39,8 @@ ROOT_FS_OPS:
dd ext2_fs_ops
extern ntfs_fs_ops
dd ntfs_fs_ops
+ extern xfs_fs_ops
+ dd xfs_fs_ops
extern btrfs_fs_ops
dd btrfs_fs_ops
dd 0
diff --git a/core/legacynet/core.c b/core/legacynet/core.c
new file mode 100644
index 00000000..94da6496
--- /dev/null
+++ b/core/legacynet/core.c
@@ -0,0 +1,228 @@
+#include <syslinux/pxe_api.h>
+#include <com32.h>
+#include <core.h>
+#include <net.h>
+#include <pxe.h>
+#include <minmax.h>
+
+/* Common receive buffer */
+static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
+
+extern uint16_t get_port(void);
+extern void free_port(uint16_t);
+
+const struct url_scheme url_schemes[] = {
+ { "tftp", tftp_open, 0 },
+ { NULL, NULL, 0 }
+};
+
+/**
+ * Open a socket
+ *
+ * @param:socket, the socket to open
+ * @param:proto, the protocol of the new connection
+ *
+ * @out: error code, 0 on success, -1 on failure
+ */
+int net_core_open(struct pxe_pvt_inode *socket __unused,
+ enum net_core_proto proto)
+{
+ struct net_private_tftp *priv = &socket->net.tftp;
+
+ /* The legacy stack only supports UDP */
+ if (proto != NET_CORE_UDP)
+ return -1;
+
+ /* Allocate local UDP port number */
+ priv->localport = get_port();
+
+ return 0;
+}
+
+/**
+ * Close a socket
+ *
+ * @param:socket, the socket to open
+ */
+void net_core_close(struct pxe_pvt_inode *socket)
+{
+ struct net_private_tftp *priv = &socket->net.tftp;
+
+ if (priv->localport)
+ free_port(priv->localport);
+}
+
+/**
+ * Establish a connection on an open socket
+ *
+ * @param:socket, the open socket
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+ uint16_t port)
+{
+ struct net_private_tftp *priv = &socket->net.tftp;
+
+ socket->tftp_remoteport = htons(port);
+ priv->remoteip = ip;
+
+}
+
+/**
+ * Tear down a connection on an open socket
+ *
+ * @param:socket, the open socket
+ */
+void net_core_disconnect(struct pxe_pvt_inode *socket __unused)
+{
+}
+
+/**
+ * Read data from the network stack
+ *
+ * @param:socket, the open socket
+ * @param:buf, location of buffer to store data
+ * @param:buf_len, size of buffer
+
+ * @out: src_ip, ip address of the data source
+ * @out: src_port, port number of the data source, host-byte order
+ */
+int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+ uint32_t *src_ip, uint16_t *src_port)
+{
+ static __lowmem struct s_PXENV_UDP_READ udp_read;
+ struct net_private_tftp *priv = &socket->net.tftp;
+ uint16_t bytes;
+ int err;
+
+ udp_read.status = 0;
+ udp_read.buffer = FAR_PTR(packet_buf);
+ udp_read.buffer_size = PKTBUF_SIZE;
+ udp_read.dest_ip = IPInfo.myip;
+ udp_read.d_port = priv->localport;
+
+ err = pxe_call(PXENV_UDP_READ, &udp_read);
+ if (err)
+ return err;
+
+ if (udp_read.status)
+ return udp_read.status;
+
+ bytes = min(udp_read.buffer_size, *buf_len);
+ memcpy(buf, packet_buf, bytes);
+
+ *src_ip = udp_read.src_ip;
+ *src_port = ntohs(udp_read.s_port);
+ *buf_len = bytes;
+
+ return 0;
+}
+
+/**
+ * Send a UDP packet.
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ */
+void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+{
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ struct net_private_tftp *priv = &socket->net.tftp;
+ void *lbuf;
+ uint16_t tid;
+
+ lbuf = lmalloc(len);
+ if (!lbuf)
+ return;
+
+ memcpy(lbuf, data, len);
+
+ tid = priv->localport; /* TID(local port No) */
+ udp_write.buffer = FAR_PTR(lbuf);
+ udp_write.ip = priv->remoteip;
+ udp_write.gw = gateway(udp_write.ip);
+ udp_write.src_port = tid;
+ udp_write.dst_port = socket->tftp_remoteport;
+ udp_write.buffer_size = len;
+
+ pxe_call(PXENV_UDP_WRITE, &udp_write);
+
+ lfree(lbuf);
+}
+
+/**
+ * Send a UDP packet to a destination
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_sendto(struct pxe_pvt_inode *socket, const void *data, size_t len,
+ uint32_t ip, uint16_t port)
+{
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ struct net_private_tftp *priv = &socket->net.tftp;
+ void *lbuf;
+ uint16_t tid;
+
+ lbuf = lmalloc(len);
+ if (!lbuf)
+ return;
+
+ memcpy(lbuf, data, len);
+
+ tid = priv->localport; /* TID(local port No) */
+ udp_write.buffer = FAR_PTR(lbuf);
+ udp_write.ip = ip;
+ udp_write.gw = gateway(udp_write.ip);
+ udp_write.src_port = tid;
+ udp_write.dst_port = htons(port);
+ udp_write.buffer_size = len;
+
+ pxe_call(PXENV_UDP_WRITE, &udp_write);
+
+ lfree(lbuf);
+}
+
+
+/**
+ * Network stack-specific initialization
+ *
+ * Initialize UDP stack
+ */
+void net_core_init(void)
+{
+ int err;
+ static __lowmem struct s_PXENV_UDP_OPEN udp_open;
+ udp_open.src_ip = IPInfo.myip;
+ err = pxe_call(PXENV_UDP_OPEN, &udp_open);
+ if (err || udp_open.status) {
+ printf("Failed to initialize UDP stack ");
+ printf("%d\n", udp_open.status);
+ kaboom();
+ }
+}
+
+void probe_undi(void)
+{
+}
+
+void pxe_init_isr(void)
+{
+}
+
+int reset_pxe(void)
+{
+ static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
+ int err = 0;
+
+ pxe_idle_cleanup();
+
+ pxe_call(PXENV_UDP_CLOSE, &udp_close);
+
+ return err;
+}
diff --git a/core/legacynet/dnsresolv.c b/core/legacynet/dnsresolv.c
new file mode 100644
index 00000000..fdbe795c
--- /dev/null
+++ b/core/legacynet/dnsresolv.c
@@ -0,0 +1,388 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include "pxe.h"
+
+/* DNS CLASS values we care about */
+#define CLASS_IN 1
+
+/* DNS TYPE values we care about */
+#define TYPE_A 1
+#define TYPE_CNAME 5
+
+/*
+ * The DNS header structure
+ */
+struct dnshdr {
+ uint16_t id;
+ uint16_t flags;
+ /* number of entries in the question section */
+ uint16_t qdcount;
+ /* number of resource records in the answer section */
+ uint16_t ancount;
+ /* number of name server resource records in the authority records section*/
+ uint16_t nscount;
+ /* number of resource records in the additional records section */
+ uint16_t arcount;
+} __attribute__ ((packed));
+
+/*
+ * The DNS query structure
+ */
+struct dnsquery {
+ uint16_t qtype;
+ uint16_t qclass;
+} __attribute__ ((packed));
+
+/*
+ * The DNS Resource recodes structure
+ */
+struct dnsrr {
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t rdlength; /* The lenght of this rr data */
+ char rdata[];
+} __attribute__ ((packed));
+
+
+#define DNS_PORT htons(53) /* Default DNS port */
+#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */
+
+uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
+
+
+/*
+ * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
+ * number of dots encountered. On return, *dst is updated.
+ */
+int dns_mangle(char **dst, const char *p)
+{
+ char *q = *dst;
+ char *count_ptr;
+ char c;
+ int dots = 0;
+
+ count_ptr = q;
+ *q++ = 0;
+
+ while (1) {
+ c = *p++;
+ if (c == 0 || c == ':' || c == '/')
+ break;
+ if (c == '.') {
+ dots++;
+ count_ptr = q;
+ *q++ = 0;
+ continue;
+ }
+
+ *count_ptr += 1;
+ *q++ = c;
+ }
+
+ if (*count_ptr)
+ *q++ = 0;
+
+ /* update the strings */
+ *dst = q;
+ return dots;
+}
+
+
+/*
+ * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
+ * is allowed pointers relative to a packet in buf.
+ *
+ */
+static bool dns_compare(const void *s1, const void *s2, const void *buf)
+{
+ const uint8_t *q = s1;
+ const uint8_t *p = s2;
+ unsigned int c0, c1;
+
+ while (1) {
+ c0 = p[0];
+ if (c0 >= 0xc0) {
+ /* Follow pointer */
+ c1 = p[1];
+ p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
+ } else if (c0) {
+ c0++; /* Include the length byte */
+ if (memcmp(q, p, c0))
+ return false;
+ q += c0;
+ p += c0;
+ } else {
+ return *q == 0;
+ }
+ }
+}
+
+/*
+ * Copy a DNS label into a buffer, considering the possibility that we might
+ * have to follow pointers relative to "buf".
+ * Returns a pointer to the first free byte *after* the terminal null.
+ */
+static void *dns_copylabel(void *dst, const void *src, const void *buf)
+{
+ uint8_t *q = dst;
+ const uint8_t *p = src;
+ unsigned int c0, c1;
+
+ while (1) {
+ c0 = p[0];
+ if (c0 >= 0xc0) {
+ /* Follow pointer */
+ c1 = p[1];
+ p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
+ } else if (c0) {
+ c0++; /* Include the length byte */
+ memcpy(q, p, c0);
+ p += c0;
+ q += c0;
+ } else {
+ *q++ = 0;
+ return q;
+ }
+ }
+}
+
+/*
+ * Skip past a DNS label set in DS:SI
+ */
+static char *dns_skiplabel(char *label)
+{
+ uint8_t c;
+
+ while (1) {
+ c = *label++;
+ if (c >= 0xc0)
+ return ++label; /* pointer is two bytes */
+ if (c == 0)
+ return label;
+ label += c;
+ }
+}
+
+extern const uint8_t TimeoutTable[];
+extern uint16_t get_port(void);
+extern void free_port(uint16_t port);
+
+/*
+ * parse the ip_str and return the ip address with *res.
+ * return true if the whole string was consumed and the result
+ * was valid.
+ *
+ */
+static bool parse_dotquad(const char *ip_str, uint32_t *res)
+{
+ const char *p = ip_str;
+ uint8_t part = 0;
+ uint32_t ip = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ while (is_digit(*p)) {
+ part = part * 10 + *p - '0';
+ p++;
+ }
+ if (i != 3 && *p != '.')
+ return false;
+
+ ip = (ip << 8) | part;
+ part = 0;
+ p++;
+ }
+ p--;
+
+ *res = htonl(ip);
+ return *p == '\0';
+}
+
+/*
+ * Actual resolver function
+ * Points to a null-terminated or :-terminated string in _name_
+ * and returns the ip addr in _ip_ if it exists and can be found.
+ * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
+ *
+ * XXX: probably need some caching here.
+ */
+__export uint32_t dns_resolv(const char *name)
+{
+ static char __lowmem DNSSendBuf[PKTBUF_SIZE];
+ static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
+ char *p;
+ int err;
+ int dots;
+ int same;
+ int rd_len;
+ int ques, reps; /* number of questions and replies */
+ uint8_t timeout;
+ const uint8_t *timeout_ptr = TimeoutTable;
+ uint32_t oldtime;
+ uint32_t srv;
+ uint32_t *srv_ptr;
+ struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
+ struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
+ struct dnsquery *query;
+ struct dnsrr *rr;
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ static __lowmem struct s_PXENV_UDP_READ udp_read;
+ uint16_t local_port;
+ uint32_t result = 0;
+
+ /*
+ * Return failure on an empty input... this can happen during
+ * some types of URL parsing, and this is the easiest place to
+ * check for it.
+ */
+ if (!name || !*name)
+ return 0;
+
+ /* If it is a valid dot quad, just return that value */
+ if (parse_dotquad(name, &result))
+ return result;
+
+ /* Make sure we have at least one valid DNS server */
+ if (!dns_server[0])
+ return 0;
+
+ /* Get a local port number */
+ local_port = get_port();
+
+ /* First, fill the DNS header struct */
+ hd1->id++; /* New query ID */
+ hd1->flags = htons(0x0100); /* Recursion requested */
+ hd1->qdcount = htons(1); /* One question */
+ hd1->ancount = 0; /* No answers */
+ hd1->nscount = 0; /* No NS */
+ hd1->arcount = 0; /* No AR */
+
+ p = DNSSendBuf + sizeof(struct dnshdr);
+ dots = dns_mangle(&p, name); /* store the CNAME */
+
+ if (!dots) {
+ p--; /* Remove final null */
+ /* Uncompressed DNS label set so it ends in null */
+ p = stpcpy(p, LocalDomain);
+ }
+
+ /* Fill the DNS query packet */
+ query = (struct dnsquery *)p;
+ query->qtype = htons(TYPE_A);
+ query->qclass = htons(CLASS_IN);
+ p += sizeof(struct dnsquery);
+
+ /* Now send it to name server */
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ srv_ptr = dns_server;
+ while (timeout) {
+ srv = *srv_ptr++;
+ if (!srv) {
+ srv_ptr = dns_server;
+ srv = *srv_ptr++;
+ }
+
+ udp_write.status = 0;
+ udp_write.ip = srv;
+ udp_write.gw = gateway(srv);
+ udp_write.src_port = local_port;
+ udp_write.dst_port = DNS_PORT;
+ udp_write.buffer_size = p - DNSSendBuf;
+ udp_write.buffer = FAR_PTR(DNSSendBuf);
+ err = pxe_call(PXENV_UDP_WRITE, &udp_write);
+ if (err || udp_write.status)
+ continue;
+
+ oldtime = jiffies();
+ do {
+ if (jiffies() - oldtime >= timeout)
+ goto again;
+
+ udp_read.status = 0;
+ udp_read.src_ip = srv;
+ udp_read.dest_ip = IPInfo.myip;
+ udp_read.s_port = DNS_PORT;
+ udp_read.d_port = local_port;
+ udp_read.buffer_size = PKTBUF_SIZE;
+ udp_read.buffer = FAR_PTR(DNSRecvBuf);
+ err = pxe_call(PXENV_UDP_READ, &udp_read);
+ } while (err || udp_read.status || hd2->id != hd1->id);
+
+ if ((hd2->flags ^ 0x80) & htons(0xf80f))
+ goto badness;
+
+ ques = htons(hd2->qdcount); /* Questions */
+ reps = htons(hd2->ancount); /* Replies */
+ p = DNSRecvBuf + sizeof(struct dnshdr);
+ while (ques--) {
+ p = dns_skiplabel(p); /* Skip name */
+ p += 4; /* Skip question trailer */
+ }
+
+ /* Parse the replies */
+ while (reps--) {
+ same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
+ p, DNSRecvBuf);
+ p = dns_skiplabel(p);
+ rr = (struct dnsrr *)p;
+ rd_len = ntohs(rr->rdlength);
+ if (same && ntohs(rr->class) == CLASS_IN) {
+ switch (ntohs(rr->type)) {
+ case TYPE_A:
+ if (rd_len == 4) {
+ result = *(uint32_t *)rr->rdata;
+ goto done;
+ }
+ break;
+ case TYPE_CNAME:
+ dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
+ rr->rdata, DNSRecvBuf);
+ /*
+ * We should probably rescan the packet from the top
+ * here, and technically we might have to send a whole
+ * new request here...
+ */
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* not the one we want, try next */
+ p += sizeof(struct dnsrr) + rd_len;
+ }
+
+ badness:
+ /*
+ *
+ ; We got back no data from this server.
+ ; Unfortunately, for a recursive, non-authoritative
+ ; query there is no such thing as an NXDOMAIN reply,
+ ; which technically means we can't draw any
+ ; conclusions. However, in practice that means the
+ ; domain doesn't exist. If this turns out to be a
+ ; problem, we may want to add code to go through all
+ ; the servers before giving up.
+
+ ; If the DNS server wasn't capable of recursion, and
+ ; isn't capable of giving us an authoritative reply
+ ; (i.e. neither AA or RA set), then at least try a
+ ; different setver...
+ */
+ if (hd2->flags == htons(0x480))
+ continue;
+
+ break; /* failed */
+
+ again:
+ continue;
+ }
+
+done:
+ free_port(local_port); /* Return port number to the free pool */
+
+ return result;
+}
diff --git a/core/legacynet/idle.c b/core/legacynet/idle.c
new file mode 100644
index 00000000..e0892379
--- /dev/null
+++ b/core/legacynet/idle.c
@@ -0,0 +1,112 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include "pxe.h"
+
+static int pxe_idle_poll(void)
+{
+ static __lowmem char junk_pkt[PKTBUF_SIZE];
+ static __lowmem t_PXENV_UDP_READ read_buf;
+
+ memset(&read_buf, 0, sizeof read_buf);
+
+ read_buf.src_ip = 0; /* Any destination */
+ read_buf.dest_ip = IPInfo.myip;
+ read_buf.s_port = 0; /* Any source port */
+ read_buf.d_port = htons(9); /* Discard port (not used...) */
+ read_buf.buffer_size = sizeof junk_pkt;
+ read_buf.buffer = FAR_PTR(junk_pkt);
+
+ pxe_call(PXENV_UDP_READ, &read_buf);
+
+ return 0;
+}
+
+static uint32_t pxe_detect_nic_type(void)
+{
+ static __lowmem t_PXENV_UNDI_GET_NIC_TYPE nic_type;
+
+ if (pxe_call(PXENV_UNDI_GET_NIC_TYPE, &nic_type))
+ return -1; /* Unknown NIC */
+
+ if (nic_type.NicType != PCI_NIC && nic_type.NicType != CardBus_NIC)
+ return -1; /* Not a PCI NIC */
+
+ /*
+ * Return VID:DID as a single number, with the VID in the high word
+ * -- this is opposite from the usual order, but it makes it easier to
+ * enforce that the table is sorted.
+ */
+ return (nic_type.info.pci.Vendor_ID << 16) + nic_type.info.pci.Dev_ID;
+}
+
+#define PCI_DEV(vid, did) (((vid) << 16) + (did))
+
+/* This array should be sorted!! */
+static const uint32_t pxe_need_idle_drain[] =
+{
+ /*
+ * Older Broadcom NICs: they need receive calls on idle to avoid
+ * FIFO stalls.
+ */
+ PCI_DEV(0x14e4, 0x1659), /* BCM5721 */
+ PCI_DEV(0x14e4, 0x165a), /* BCM5722 */
+ PCI_DEV(0x14e4, 0x165b), /* BCM5723 */
+ PCI_DEV(0x14e4, 0x1668), /* BCM5714 */
+ PCI_DEV(0x14e4, 0x1669), /* BCM5714S */
+ PCI_DEV(0x14e4, 0x166a), /* BCM5780 */
+ PCI_DEV(0x14e4, 0x1673), /* BCM5755M */
+ PCI_DEV(0x14e4, 0x1674), /* BCM5756ME */
+ PCI_DEV(0x14e4, 0x1678), /* BCM5715 */
+ PCI_DEV(0x14e4, 0x1679), /* BCM5715S */
+ PCI_DEV(0x14e4, 0x167b), /* BCM5755 */
+};
+
+void pxe_idle_init(void)
+{
+ uint32_t dev_id = pxe_detect_nic_type();
+ int l, h;
+ bool found;
+
+ l = 0;
+ h = sizeof pxe_need_idle_drain / sizeof pxe_need_idle_drain[0] - 1;
+
+ found = false;
+ while (h >= l) {
+ int x = (l+h) >> 1;
+ uint32_t id = pxe_need_idle_drain[x];
+
+ if (id == dev_id) {
+ found = true;
+ break;
+ } else if (id < dev_id) {
+ l = x+1;
+ } else {
+ h = x-1;
+ }
+ }
+
+ if (found)
+ idle_hook_func = pxe_idle_poll;
+}
+
+void pxe_idle_cleanup(void)
+{
+ idle_hook_func = NULL;
+}
diff --git a/core/fs/pxe/portnum.c b/core/legacynet/portnum.c
index 19af0cd0..e10af29e 100644
--- a/core/fs/pxe/portnum.c
+++ b/core/legacynet/portnum.c
@@ -1,5 +1,5 @@
/* ----------------------------------------------------------------------- *
- *
+ *
* Copyright 2010 Intel Corporation; author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
diff --git a/core/loadhigh.inc b/core/loadhigh.inc
deleted file mode 100644
index 89de5e8d..00000000
--- a/core/loadhigh.inc
+++ /dev/null
@@ -1,60 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; loadhigh.inc
-;;
-;; Load a file into high memory
-;;
-
- section .text16
-
-;
-; load_high: loads (the remainder of) a file into high memory.
-;
-; Assumes CS == DS.
-;
-; Inputs: SI = file handle/cluster pointer
-; EDI = target address in high memory
-; EAX = maximum number of bytes to load
-; DX = zero-padding mask (e.g. 0003h for pad to dword)
-; BX = subroutine to call at the top of each loop
-; (to print status and check for abort)
-; [MyHighMemSize] = maximum load address
-;
-; Outputs: SI = file handle/cluster pointer
-; EBX = first untouched address (not including padding)
-; EDI = first untouched address (including padding)
-; CF = reached EOF
-;
- extern pm_load_high
-load_high:
- push ebp
- mov ebp,[MyHighMemSize]
- pm_call pm_load_high
- pop ebp
- jo .overflow
- ret
-
-.overflow: mov si,err_nohighmem
- jmp abort_load
-
- section .data16
-err_nohighmem db CR, LF
- db 'Not enough memory to load specified image.', CR, LF, 0
-
- section .bss16
- alignb 2
-PauseBird resw 1
-
- section .text16
diff --git a/core/localboot.c b/core/localboot.c
new file mode 100644
index 00000000..0f4b5820
--- /dev/null
+++ b/core/localboot.c
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ */
+#include <sys/cpu.h>
+#include <sys/io.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <bios.h>
+#include <graphics.h>
+
+/*
+ * localboot.c
+ *
+ * Boot from a local disk, or invoke INT 18h.
+ */
+
+#define LOCALBOOT_MSG "Booting from local disk..."
+
+#define retry_count 16
+
+extern void local_boot16(void);
+
+/*
+ * Boot a specified local disk. AX specifies the BIOS disk number; or
+ * -1 in case we should execute INT 18h ("next device.")
+ */
+__export void local_boot(int16_t ax)
+{
+ com32sys_t ireg, oreg;
+ int i;
+
+ syslinux_force_text_mode();
+
+ writestr(LOCALBOOT_MSG);
+ crlf();
+ cleanup_hardware();
+
+ if (ax == -1) {
+ /* Hope this does the right thing */
+ __intcall(0x18, &zero_regs, NULL);
+
+ /* If we returned, oh boy... */
+ kaboom();
+ }
+
+ /*
+ * Load boot sector from the specified BIOS device and jump to
+ * it.
+ */
+ ireg.edx.b[0] = ax & 0xff;
+ ireg.eax.w[0] = 0; /* Reset drive */
+ __intcall(0x13, &ireg, NULL);
+
+ ireg.eax.w[0] = 0x0201; /* Read one sector */
+ ireg.ecx.w[0] = 0x0001; /* C/H/S = 0/0/1 (first sector) */
+ ireg.ebx.w[0] = OFFS(trackbuf);
+ ireg.es = SEG(trackbuf);
+
+ for (i = 0; i < retry_count; i++) {
+ __intcall(0x13, &ireg, &oreg);
+
+ if (!(oreg.eflags.l & EFLAGS_CF))
+ break;
+ }
+
+ if (i == retry_count)
+ kaboom();
+
+ cli(); /* Abandon hope, ye who enter here */
+ memcpy((void *)0x07C00, trackbuf, 512);
+
+ ireg.esi.w[0] = OFFS(trackbuf);
+ ireg.edi.w[0] = 0x07C00;
+ ireg.edx.w[0] = ax;
+ call16(local_boot16, &ireg, NULL);
+}
+
+void pm_local_boot(com32sys_t *regs)
+{
+ local_boot(regs->eax.w[0]);
+}
diff --git a/core/localboot.inc b/core/localboot.inc
index 1fe3102d..b7840427 100644
--- a/core/localboot.inc
+++ b/core/localboot.inc
@@ -1,73 +1,7 @@
-; -----------------------------------------------------------------------
-;
-; Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
-; Boston MA 02110-1301, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; -----------------------------------------------------------------------
-
-;
-; localboot.inc
-;
-; Boot from a local disk, or invoke INT 18h.
-;
-
-;
-; Boot a specified local disk. AX specifies the BIOS disk number; or
-; -1 in case we should execute INT 18h ("next device.")
-;
section .text16
-
-local_boot:
- call vgaclearmode
- RESET_STACK_AND_SEGS dx ; dx <- 0
- mov fs,dx
- mov gs,dx
- mov si,localboot_msg
- call writestr
- call cleanup_hardware
- cmp ax,-1
- je .int18
-
- ; Load boot sector from the specified BIOS device and jump to it.
- mov dl,al
- xor dh,dh
- push dx
- xor ax,ax ; Reset drive
- int 13h
- mov ax,0201h ; Read one sector
- mov cx,0001h ; C/H/S = 0/0/1 (first sector)
- mov bx,trackbuf
- mov bp,retry_count
-.again:
- pusha
- int 13h
- popa
- jnc .ok
- dec bp
- jnz .again
- jmp kaboom ; Failure...
-.ok:
- pop dx
- cli ; Abandon hope, ye who enter here
- mov si,trackbuf
- mov di,07C00h
- mov cx,512 ; Probably overkill, but should be safe
- rep movsd
- mov ss,cx ; SS <- 0
+ global local_boot16:function hidden
+local_boot16:
+ mov cx,0
+ mov ss,cx
mov sp,7C00h
- jmp 0:07C00h ; Jump to new boot sector
-
-.int18:
- int 18h ; Hope this does the right thing...
- jmp kaboom ; If we returned, oh boy...
-
- section .data16
-localboot_msg db 'Booting from local disk...', CR, LF, 0
-
- section .text16
-
+ jmp 0:07C00h
diff --git a/core/lwip/CHANGELOG b/core/lwip/CHANGELOG
new file mode 100644
index 00000000..6e27a66b
--- /dev/null
+++ b/core/lwip/CHANGELOG
@@ -0,0 +1,3050 @@
+HISTORY
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ New features:
+
+
+ ++ Bugfixes:
+
+
+
+
+(STABLE-1.4.0)
+
+ ++ New features:
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and
+ calculate it in tcp_zero_window_probe (the only place where it was used).
+
+ 2010-11-21: Simon Goldschmidt
+ * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif
+ (fixes bug #31525).
+
+ 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage)
+ * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for
+ IP_MULTICAST_LOOP at socket- and raw-API level.
+
+ 2010-06-16: Simon Goldschmidt
+ * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow
+ link-layer-addressed UDP traffic to be received while a netif is down (just
+ like DHCP during configuration)
+
+ 2010-05-22: Simon Goldschmidt
+ * many many files: bug #27352: removed packing from ip_addr_t, the packed
+ version is now only used in protocol headers. Added global storage for
+ current src/dest IP address while in input functions.
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h: task #10391: Add preprocessor-macros for compile-time htonl
+ calculation (and use them throughout the stack where applicable)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool
+ instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own
+ MEMP pool instead of the heap
+
+ 2010-05-13: Simon Goldschmidt
+ * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added
+ new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast
+ packets to more than one pcb.
+
+ 2010-05-02: Simon Goldschmidt
+ * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
+ UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-04-30: Simon Goldschmidt
+ * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that
+ take a precalculated checksum, added pbuf_fill_chksum() to copy data
+ into a pbuf and at the same time calculating the checksum for that data
+
+ 2010-04-29: Simon Goldschmidt
+ * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying
+ 2-byte-aligned IP addresses and MAC addresses
+
+ 2010-04-28: Patch by Bill Auerbach
+ * ip.c: Inline generating IP checksum to save a function call
+
+ 2010-04-14: Simon Goldschmidt
+ * tcpip.h/.c, timers.c: Added an overridable define to get informed when the
+ tcpip_thread processes messages or timeouts to implement a watchdog.
+
+ 2010-03-28: Simon Goldschmidt
+ * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing
+ fragment if LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-03-27: Simon Goldschmidt
+ * etharp.c: Speedup TX by moving code from find_entry to etharp_output/
+ etharp_query to prevent unnecessary function calls (inspired by
+ patch #7135).
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code
+ since the linker cannot do this automatically to save space.
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, etharp.c/.h: Added support for static ARP table entries
+
+ 2010-03-14: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum
+ when creating TCP segments, not when (re-)transmitting them.
+
+ 2010-03-07: Simon Goldschmidt
+ * sockets.c: bug #28775 (select/event_callback: only check select_cb_list
+ on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code.
+ This should speed up receiving data on sockets as the select code in
+ event_callback is only executed when select is waiting.
+
+ 2010-03-06: Simon Goldschmidt
+ * tcp_out.c: task #7013 (Create option to have all packets delivered to
+ netif->output in one piece): Always copy to try to create single pbufs
+ in tcp_write.
+
+ 2010-03-06: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
+ by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
+ for tcp netconns to receive pbufs, not netbufs; use that function
+ for tcp sockets.
+
+ 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
+ * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
+ Work on tcp_enqueue: Don't waste memory when chaining segments,
+ added option TCP_OVERSIZE to prevent creating many small pbufs when
+ calling tcp_write with many small blocks of data. Instead, pbufs are
+ allocated larger than needed and the space is used for later calls to
+ tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * stats.c/.h: Added const char* name to mem- and memp-stats for easier
+ debugging.
+
+ 2010-02-21: Simon Goldschmidt
+ * tcp.h (and usages), added tcp_impl.h: Splitted API and internal
+ implementation of tcp to make API usage cleare to application programmers
+
+ 2010-02-14: Simon Goldschmidt/Stephane Lesage
+ * ip_addr.h: Improved some defines working on ip addresses, added faster
+ macro to copy addresses that cannot be NULL
+
+ 2010-02-13: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non-
+ blocking send operation)
+
+ 2010-02-12: Simon Goldschmidt
+ * sockets.c/.h: Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ 2010-02-12: Simon Goldschmidt
+ * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated
+ memory): added autoip_set_struct() and dhcp_set_struct() to let autoip
+ and dhcp work with user-allocated structs instead of callin mem_malloc
+
+ 2010-02-12: Simon Goldschmidt/Jeff Barber
+ * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has
+ SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT
+
+ 2010-02-12: Simon Goldschmidt
+ * sys layer: task #10139 (Prefer statically allocated memory): converted
+ mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t;
+ converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+ task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX
+ to let sys.h use binary semaphores instead of mutexes - as before)
+
+ 2010-02-09: Simon Goldschmidt (Simon Kallweit)
+ * timers.c/.h: Added function sys_restart_timeouts() from patch #7085
+ (Restart system timeout handling)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into
+ netif.c) - loopif does not have to be created by the port any more,
+ just define LWIP_HAVE_LOOPIF to 1.
+
+ 2010-02-08: Simon Goldschmidt
+ * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa
+ inet_ntoa_r/ipaddr_ntoa_r
+
+ 2010-02-08: Simon Goldschmidt
+ * netif.h: Added netif_s/get_igmp_mac_filter() macros
+
+ 2010-02-05: Simon Goldschmidt
+ * netif.h: Added function-like macros to get/set the hostname on a netif
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to
+ make changing the actual implementation behind the typedef easier.
+
+ 2010-02-01: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool
+ for allocating memory when getaddrinfo() is called.
+
+ 2010-01-31: Simon Goldschmidt
+ * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse
+ them once instead of parsing for every option. This also removes
+ the need for mem_malloc from dhcp_recv and makes it possible to
+ correctly retrieve the BOOTP file.
+
+ 2010-01-30: simon Goldschmidt
+ * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect
+ the sockets array.
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, api_msg.c, sockets.c: Added except set support in select
+ (patch #6860)
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c:
+ Add non-blocking support for connect (partly from patch #6860),
+ plus many cleanups in socket & netconn API.
+
+ 2010-01-27: Simon Goldschmidt
+ * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
+ to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605
+
+ 2010-01-26: Simon Goldschmidt
+ * snmp: Use memp pools for snmp instead of the heap; added 4 new pools.
+
+ 2010-01-14: Simon Goldschmidt
+ * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback
+ by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback()
+
+ 2010-01-13: Simon Goldschmidt
+ * mem.c: The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+ (patch #6966 and bug #26133)
+
+ 2010-01-10: Simon Goldschmidt (Bill Auerbach)
+ * opt.h, memp.c: patch #6822 (Add option to place memory pools in
+ separate arrays)
+
+ 2010-01-10: Simon Goldschmidt
+ * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define
+ LWIP_RAND() for lwip-wide randomization (to be defined in cc.h)
+
+ 2009-12-31: Simon Goldschmidt
+ * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h
+ added timers.c/.h: Separated timer implementation from semaphore/mbox
+ implementation, moved timer implementation to timers.c/.h, timers are
+ now only called from tcpip_thread or by explicitly checking them.
+ (TASK#7235)
+
+ 2009-12-27: Simon Goldschmidt
+ * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option
+ LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE)
+
+
+ ++ Bugfixes:
+
+ 2011-04-20: Simon Goldschmidt
+ * sys_arch.txt: sys_arch_timeouts() is not needed any more.
+
+ 2011-04-13: Simon Goldschmidt
+ * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by
+ using ports in the IANA private/dynamic range (49152 through 65535).
+
+ 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl:
+ * etharp.h/.c: Fixed broken VLAN support.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp
+ pcbs) by checking if the pcb was bound (local_port != 0).
+
+ 2011-03-27: Simon Goldschmidt
+ * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice)
+
+ 2011-03-27: Simon Goldschmidt
+ * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and
+ raw pcbs with LWIP_TCPIP_CORE_LOCKING==1.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route
+ is present never times out) by starting retransmission timer before checking
+ route.
+
+ 2011-03-22: Simon Goldschmidt
+ * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only
+ calling sio_read_abort() if the file descriptor is valid.
+
+ 2011-03-14: Simon Goldschmidt
+ * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect
+ more than once can render a socket useless) since it mainly involves changing
+ "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal.
+
+ 2011-03-13: Simon Goldschmidt
+ * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing
+ err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN:
+ use EALRADY instead of -1
+
+ 2011-03-13: Simon Goldschmidt
+ * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the
+ connection has been aborted by err_tcp (since this is not a normal closing
+ procedure).
+
+ 2011-03-13: Simon Goldschmidt
+ * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind
+ with pcb->state != CLOSED
+
+ 2011-02-17: Simon Goldschmidt
+ * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in
+ documentation
+
+ 2011-02-17: Simon Goldschmidt
+ * many files: Added missing U/UL modifiers to fix 16-bit-arch portability.
+
+ 2011-01-24: Simon Goldschmidt
+ * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
+
+ 2010-12-02: Simon Goldschmidt
+ * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.
+
+ 2010-11-23: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for
+ LWIP_SO_RCVBUF and ioctl/FIONREAD.
+
+ 2010-11-23: Simon Goldschmidt
+ * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at
+ least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet.
+
+ 2010-11-23: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after
+ refusing 'refused_data' again.
+
+ 2010-11-22: Simon Goldschmidt
+ * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS
+ after a successful nonblocking connection.
+
+ 2010-11-22: Simon Goldschmidt
+ * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr
+ must be sent link-local
+
+ 2010-11-22: Simon Goldschmidt
+ * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for
+ LWIP_TIMERS==0
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to
+ resemble other stacks.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else
+ no-copy TCP writes will never succeed.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does
+ not match documentation: return ERR_ARG instead of ERR_VAL if not
+ initialized or wrong argument.
+
+ 2010-10-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16
+
+ 2010-10-05: Simon Goldschmidt
+ * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when
+ replugging the network cable after an AutoIP address was assigned.
+
+ 2010-08-10: Simon Goldschmidt
+ * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs
+
+ 2010-08-03: Simon Goldschmidt
+ * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625)
+
+ 2010-08-01: Simon Goldschmidt (patch by Greg Renda)
+ * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big
+ endian architectures)
+
+ 2010-07-28: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP
+ disabled.
+
+ 2010-07-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no
+ harm but never did anything
+
+ 2010-07-21: Simon Goldschmidt
+ * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not
+ add IP options)
+
+ 2010-07-16: Kieran Mansley
+ * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator
+
+ 2010-07-10: Simon Goldschmidt
+ * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options
+
+ 2010-06-30: Simon Goldschmidt
+ * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in
+ netconn_delete)
+
+ 2010-06-28: Kieran Mansley
+ * timers.c remove unportable printing of C function pointers
+
+ 2010-06-24: Simon Goldschmidt
+ * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag
+ NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading
+
+ 2010-06-24: Simon Goldschmidt
+ * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly
+ implemented shutdown at socket level.
+
+ 2010-06-21: Simon Goldschmidt
+ * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
+ problems with zero-copy DMA MACs) by adding custom pbufs and implementing
+ custom pbufs that reference other (original) pbufs. Additionally set
+ IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side.
+
+ 2010-06-15: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses
+
+ 2010-06-14: Simon Goldschmidt
+ * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses
+
+ 2010-06-12: Simon Goldschmidt
+ * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop
+ state
+
+ 2010-05-17: Simon Goldschmidt
+ * netdb.c: Correctly NULL-terminate h_addr_list
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent
+ "symbol already defined" i.e. when linking to winsock
+
+ 2010-05-05: Simon Goldschmidt
+ * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may
+ overflow)
+
+ 2010-04-21: Simon Goldschmidt
+ * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening
+ connection)
+
+ 2010-03-28: Luca Ceresoli
+ * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers
+
+ 2010-03-27: Luca Ceresoli
+ * mib2.c: patch #7130: remove meaningless const qualifiers
+
+ 2010-03-26: Simon Goldschmidt
+ * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too
+
+ 2010-03-26: Simon Goldschmidt
+ * various files: Fixed compiling with different options disabled (TCP/UDP),
+ triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled
+
+ 2010-03-25: Simon Goldschmidt
+ * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly
+
+ 2010-03-25: Simon Goldschmidt
+ * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side
+ overrunning our rcv_wnd in ooseq case.
+
+ 2010-03-22: Simon Goldschmidt
+ * tcp.c: tcp_listen() did not copy the pcb's prio.
+
+ 2010-03-19: Simon Goldschmidt
+ * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set
+
+ 2010-03-14: Simon Goldschmidt
+ * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports
+ where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h
+ and basing PBUF_LINK_HLEN on it.
+
+ 2010-03-08: Simon Goldschmidt
+ * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections
+ when assiging routable address): when checking incoming packets and
+ aborting existing connection on address change, filter out link-local
+ addresses.
+
+ 2010-03-06: Simon Goldschmidt
+ * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING
+
+ 2010-03-06: Simon Goldschmidt
+ * ipv4/ip.c: Don't try to forward link-local addresses
+
+ 2010-03-06: Simon Goldschmidt
+ * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal-
+ addresses to gw
+
+ 2010-03-05: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type
+ and state.
+
+ 2010-03-05: Simon Goldschmidt
+ * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split
+ into multiple calls to tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep
+ the implementation of DNS_USES_STATIC_BUF==1)
+
+ 2010-02-20: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
+ close() vs. shutdown(). Now the application does not get any more
+ recv callbacks after calling tcp_close(). Added tcp_shutdown().
+
+ 2010-02-19: Simon Goldschmidt
+ * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
+ confusion with realloc()
+
+ 2010-02-15: Simon Goldschmidt/Stephane Lesage
+ * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK
+ (fixes bug #28899)
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with
+ LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and
+ admin-status of a netif are up
+
+ 2010-02-14: Simon Goldschmidt
+ * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet
+ reception and is not really necessary
+
+ 2010-02-14: Simon Goldschmidt
+ * etharp.c/.h: Fixed ARP input processing: only add a new entry if a
+ request was directed as us (RFC 826, Packet Reception), otherwise
+ only update existing entries; internalized some functions
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be
+ disabled on netif used for PPPoE) by adding a new netif flag
+ (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet
+ device but prevents usage of ARP (so that ethernet_input can be used
+ for PPPoE).
+
+ 2010-02-12: Simon Goldschmidt
+ * netif.c: netif_set_link_up/down: only do something if the link state
+ actually changes
+
+ 2010-02-12: Simon Goldschmidt/Stephane Lesage
+ * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking
+ connect)
+
+ 2010-02-12: Simon Goldschmidt
+ * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h)
+
+ 2010-02-09: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110
+ (recv() makes receive window update for data that wasn't received by
+ application)
+
+ 2010-02-09: Simon Goldschmidt/Stephane Lesage
+ * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out
+ or any netconn_recv() error)
+
+ 2010-02-09: Simon Goldschmidt
+ * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c: For loopback packets, adjust the stats- and snmp-counters
+ for the loopback netif.
+
+ 2010-02-08: Simon Goldschmidt
+ * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity
+ since they are not used anywhere else.
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats
+ (patch from bug #28798)
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and
+ another bug when LWIP_RAND() returns zero.
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Use macros defined in ip_addr.h (some of them new)
+ to work with IP addresses (preparation for bug #27352 - Change ip_addr
+ from struct to typedef (u32_t) - and better code).
+
+ 2010-01-31: Simon Goldschmidt
+ * netif.c: Don't call the link-callback from netif_set_up/down() since
+ this invalidly retriggers DHCP.
+
+ 2010-01-29: Simon Goldschmidt
+ * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the
+ portability file inet.h and its contents from the stack: moved htonX-
+ functions to def.h (and the new def.c - they are not ipv4 dependent),
+ let inet.h depend on ip_addr.h and not the other way round.
+ This fixes bug #28732.
+
+ 2010-01-28: Kieran Mansley
+ * tcp.c: Ensure ssthresh >= 2*MSS
+
+ 2010-01-27: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv
+ callback can lead to accessing unallocated memory. As a consequence,
+ ERR_ABRT means the application has called tcp_abort()!
+
+ 2010-01-25: Simon Goldschmidt
+ * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY
+ not implemented in SNMP): write-only or not-accessible are still
+ returned by getnext (though not by get)
+
+ 2010-01-24: Simon Goldschmidt
+ * snmp: Renamed the private mib node from 'private' to 'mib_private' to
+ not use reserved C/C++ keywords
+
+ 2010-01-23: Simon Goldschmidt
+ * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less
+ than 1 ms
+
+ 2010-01-21: Simon Goldschmidt
+ * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called
+ if tcp_enqueue fails) both in raw- and netconn-API
+
+ 2010-01-19: Simon Goldschmidt
+ * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp
+
+ 2010-01-18: Iordan Neshev/Simon Goldschmidt
+ * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some
+ bugfix backports from 2.4.x.
+
+ 2010-01-18: Simon Goldschmidt
+ * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong
+
+ 2010-01-17: Simon Goldschmidt
+ * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c):
+ task #10102: "netconn: clean up conn->err threading issues" by adding
+ error return value to struct api_msg_msg
+
+ 2010-01-17: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept()
+ to return err_t (bugs #27709 and #28087)
+
+ 2010-01-14: Simon Goldschmidt
+ * ...: Use typedef for function prototypes throughout the stack.
+
+ 2010-01-13: Simon Goldschmidt
+ * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive
+ window = 0) by correctly draining recvmbox/acceptmbox
+
+ 2010-01-11: Simon Goldschmidt
+ * pap.c: Fixed bug #13315 (PPP PAP authentication can result in
+ erroneous callbacks) by copying the code from recent pppd
+
+ 2010-01-10: Simon Goldschmidt
+ * raw.c: Fixed bug #28506 (raw_bind should filter received packets)
+
+ 2010-01-10: Simon Goldschmidt
+ * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)())
+
+ 2010-01-08: Simon Goldschmidt
+ * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535)
+
+ 2010-01-08: Simon Goldschmidt
+ * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string
+ passed to dns_local_addhost() might be volatile
+
+ 2010-01-07: Simon Goldschmidt
+ * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too
+
+ 2010-01-06: Simon Goldschmidt
+ * netdb.h: Fixed bug #28496: missing include guards in netdb.h
+
+ 2009-12-31: Simon Goldschmidt
+ * many ppp files: Reorganised PPP source code from ucip structure to pppd
+ structure to easily compare our code against the pppd code (around v2.3.1)
+
+ 2009-12-27: Simon Goldschmidt
+ * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted
+ unit test
+
+
+(STABLE-1.3.2)
+
+ ++ New features:
+
+ 2009-10-27 Simon Goldschmidt/Stephan Lesage
+ * netifapi.c/.h: Added netifapi_netif_set_addr()
+
+ 2009-10-07 Simon Goldschmidt/Fabian Koch
+ * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to
+ support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO)
+
+ 2009-08-26 Simon Goldschmidt/Simon Kallweit
+ * slipif.c/.h: bug #26397: SLIP polling support
+
+ 2009-08-25 Simon Goldschmidt
+ * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN),
+ New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK.
+
+ 2009-08-25 Simon Goldschmidt
+ * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*)
+
+ 2009-08-24 Jakob Stoklund Olesen
+ * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond
+ to netif_set_link_up().
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state
+ to a human-readable string.
+
+ ++ Bugfixes:
+
+ 2009-12-24: Kieran Mansley
+ * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing
+ (BUG#28241)
+
+ 2009-12-06: Simon Goldschmidt
+ * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can
+ be statically allocated (like in ucip)
+
+ 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev)
+ * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT
+
+ 2009-12-03: Simon Goldschmidt
+ * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit
+ could have non-zero length
+
+ 2009-12-02: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting
+ tcp_input_pcb until after calling the pcb's callbacks
+
+ 2009-11-29: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of-
+ sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code
+
+ 2009-11-29: Simon Goldschmidt
+ * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by
+ queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending
+ segment
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle
+ algorithm at PCB level
+
+ 2009-11-22: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent
+
+ 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach)
+ * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when
+ reusing time-wait pcb
+
+ 2009-11-20: Simon Goldschmidt (patch by Albert Bartel)
+ * sockets.c: Fixed bug #28062: Data received directly after accepting
+ does not wake up select
+
+ 2009-11-11: Simon Goldschmidt
+ * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo)
+
+ 2009-10-30: Simon Goldschmidt
+ * opt.h: Increased default value for TCP_MSS to 536, updated default
+ value for TCP_WND to 4*TCP_MSS to keep delayed ACK working.
+
+ 2009-10-28: Kieran Mansley
+ * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
+ to follow algorithm from TCP/IP Illustrated
+
+ 2009-10-27: Kieran Mansley
+ * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if
+ pcb->recv is NULL to keep rcv_wnd correct)
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state
+
+ 2009-10-23: Simon Goldschmidt (David Empson)
+ * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and
+ trailing 1 byte len (SYN/FIN)
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27315: zero window probe and FIN
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c/.h: Minor code simplification (don't store received pbuf, change
+ conditional code to assert where applicable), check pbuf length before
+ testing for valid reply
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c: Removed most calls to udp_connect since they aren't necessary
+ when using udp_sendto_if() - always stay connected to IP_ADDR_ANY.
+
+ 2009-10-16: Simon Goldschmidt
+ * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop
+ valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is
+ enabled
+
+ 2009-10-15: Simon Goldschmidt (Oleg Tyshev)
+ * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit
+
+ 2009-10-15: Simon Goldschmidt
+ * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv()
+ timeout
+
+ 2009-10-15: Simon Goldschmidt
+ * autoip.c: Fixed bug #27704: autoip starts with wrong address
+ LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
+ of network byte order
+
+ 2009-10-11 Simon Goldschmidt (Jörg Kesten)
+ * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments
+ which are not consecutive when retransmitting unacked segments
+
+ 2009-10-09 Simon Goldschmidt
+ * opt.h: Fixed default values of some stats to only be enabled if used
+ Fixes bug #27338: sys_stats is defined when NO_SYS = 1
+
+ 2009-08-30 Simon Goldschmidt
+ * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK
+ function" by checking for loopback before calling ip_frag
+
+ 2009-08-25 Simon Goldschmidt
+ * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27078: Possible memory leak in pppInit()
+
+ 2009-08-23 Simon Goldschmidt
+ * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result
+ is error.
+
+ 2009-08-23 Simon Goldschmidt
+ * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF
+ Fixed wrong parenthesis, added check in init.c
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms
+
+ 2009-08-23 Simon Goldschmidt
+ * many ppp files: bug #27267: Added include to string.h where needed
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian)
+
+
+(STABLE-1.3.1)
+
+ ++ New features:
+
+ 2009-05-10 Simon Goldschmidt
+ * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option
+ LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only
+ one pbuf to help MACs that don't support scatter-gather DMA.
+
+ 2009-05-09 Simon Goldschmidt
+ * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming
+ ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+
+ 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen
+ * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive
+ extended info about the currently received packet.
+
+ 2009-04-27 Simon Goldschmidt
+ * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1
+
+ 2009-04-25 Simon Goldschmidt
+ * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next
+ bigger malloc pool if one is empty (only usable with MEM_USE_POOLS).
+
+ 2009-04-21 Simon Goldschmidt
+ * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static
+ hosts table. New configuration options DNS_LOCAL_HOSTLIST and
+ DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined
+ as an external function for lookup.
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
+ TCP timestamp options, off by default. Rework tcp_enqueue() to
+ take option flags rather than specified option data
+
+ 2009-02-18 Simon Goldschmidt
+ * cc.h: Added printf formatter for size_t: SZT_F
+
+ 2009-02-16 Simon Goldschmidt (patch by Rishi Khan)
+ * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast
+ pings
+
+ 2009-02-12 Simon Goldschmidt
+ * init.h: Added LWIP_VERSION to get the current version of the stack
+
+ 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler)
+ * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead
+ of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc
+ is otherwise used)
+
+ 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach)
+ * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial()
+ is only used by UDPLITE at present, so conditionalise it.
+
+ 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli)
+ * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP
+ "seed" address. This should reduce AUTOIP conflicts if
+ LWIP_AUTOIP_CREATE_SEED_ADDR is overridden.
+
+ 2008-10-02 Jonathan Larmour and Rishi Khan
+ * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking
+ socket.
+
+ 2008-06-30 Simon Goldschmidt
+ * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from
+ interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows
+ mem_free to run between mem_malloc iterations. Added illegal counter for
+ mem stats.
+
+ 2008-06-27 Simon Goldschmidt
+ * stats.h/.c, some other files: patch #6483: stats module improvement:
+ Added defines to display each module's statistic individually, added stats
+ defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter.
+
+ 2008-06-17 Simon Goldschmidt
+ * err.h: patch #6459: Made err_t overridable to use a more efficient type
+ (define LWIP_ERR_T in cc.h)
+
+ 2008-06-17 Simon Goldschmidt
+ * slipif.c: patch #6480: Added a configuration option for slipif for symmetry
+ to loopif
+
+ 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli)
+ * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly
+ modified version of patch # 6370: Moved loopif code to netif.c so that
+ loopback traffic is supported on all netifs (all local IPs).
+ Added option to limit loopback packets for each netifs.
+
+
+ ++ Bugfixes:
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when
+ out of window or out of order properly
+
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1
+
+ 2009-07-28 Simon Goldschmidt
+ * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s
+
+ 2009-07-27 Kieran Mansley
+ * api.h api_msg.h netdb.h sockets.h: add missing #include directives
+
+ 2009-07-09 Kieran Mansley
+ * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for
+ recv_avail and don't increment counters until message successfully
+ sent to mbox
+
+ 2009-06-25 Kieran Mansley
+ * api_msg.c api.h: BUG26722: initialise netconn write variables
+ in netconn_alloc
+
+ 2009-06-25 Kieran Mansley
+ * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set
+
+ 2009-06-25 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct
+ simultaneous close behaviour, and make snd_nxt have the same meaning
+ as in the RFCs.
+
+ 2009-05-12 Simon Goldschmidt
+ * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on
+ arp_table / uses etharp_query" by adding etharp_gratuitous()
+
+ 2009-05-12 Simon Goldschmidt
+ * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options
+ to the IP header (used by igmp_ip_output_if)
+
+ 2009-05-06 Simon Goldschmidt
+ * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if
+ defined) for SWAP_BYTES_IN_WORD to speed up checksumming.
+
+ 2009-05-05 Simon Goldschmidt
+ * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select()
+ to crash
+
+ 2009-05-04 Simon Goldschmidt
+ * init.c: snmp was not initialized in lwip_init()
+
+ 2009-05-04 Frédéric Bernon
+ * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
+
+ 2009-05-03 Simon Goldschmidt
+ * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full
+ (and unsent->next == NULL)
+
+ 2009-05-02 Simon Goldschmidt
+ * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after
+ 1.3.0 in CVS only) - fixes compilation of ppp_oe.c
+
+ 2009-05-02 Simon Goldschmidt
+ * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields
+
+ 2009-05-01 Simon Goldschmidt
+ * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets
+
+ 2009-05-01 Simon Goldschmidt
+ * ppp.c: bug #24228: Memory corruption with PPP and DHCP
+
+ 2009-04-29 Frédéric Bernon
+ * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the
+ SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception
+ of broadcast packets even when this option wasn't set. Port maintainers
+ which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h.
+ If you want this option also filter broadcast on recv operations, you also
+ have to set IP_SOF_BROADCAST_RECV=1 in opt.h.
+
+ 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen
+ * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and
+ DHCP/AUTOIP cooperation
+
+ 2009-04-25 Simon Goldschmidt, Oleg Tyshev
+ * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd
+ Fixed by sorting the unsent and unacked queues (segments are inserted at the
+ right place in tcp_output and tcp_rexmit).
+
+ 2009-04-25 Simon Goldschmidt
+ * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation
+ when debugging": memp_sizes contained the wrong sizes (including sanity
+ regions); memp pools for MEM_USE_POOLS were too small
+
+ 2009-04-24 Simon Goldschmidt, Frédéric Bernon
+ * inet.c: patch #6765: Fix a small problem with the last changes (incorrect
+ behavior, with with ip address string not ended by a '\0', a space or a
+ end of line)
+
+ 2009-04-19 Simon Goldschmidt
+ * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails,
+ pcb->err is called, not pcb->connected (with an error code).
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with
+ no-copy-tcpwrite": deallocate option data, only concat segments with same flags
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated
+ in the header pbuf, not the data pbuf)
+
+ 2009-04-18 Simon Goldschmidt
+ * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore()
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in
+
+ 2009-04-15 Simon Goldschmidt
+ * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function
+ ip_hinted_output() (for smaller code mainly)
+
+ 2009-04-15 Simon Goldschmidt
+ * inet.c: patch #6765: Supporting new line characters in inet_aton()
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option;
+ Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu
+ is big enough in dhcp_start
+
+ 2009-04-15 Simon Goldschmidt
+ * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: bug #26121: set_errno can be overridden
+
+ 2009-04-09 Kieran Mansley (patch from Luca Ceresoli <lucaceresoli>)
+ * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when
+ LWIP_TCP==0
+
+ 2009-04-09 Kieran Mansley (patch from Roy Lee <roylee17>)
+ * tcp.h: Patch#6802 Add do-while-clauses to those function like
+ macros in tcp.h
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
+ updates are calculated and sent (BUG20515)
+
+ * tcp_in.c: cope with SYN packets received during established states,
+ and retransmission of initial SYN.
+
+ * tcp_out.c: set push bit correctly when tcp segments are merged
+
+ 2009-03-27 Kieran Mansley
+ * tcp_out.c set window correctly on probes (correcting change made
+ yesterday)
+
+ 2009-03-26 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping
+ connections where no reset required (bug #25622)
+
+ * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes
+ (bug #20779)
+
+ 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach)
+ * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be
+ too small depending on MEM_ALIGNMENT
+
+ 2009-02-16 Simon Goldschmidt
+ * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard;
+ converted size argument of netconn_write to 'size_t'
+
+ 2009-02-16 Simon Goldschmidt
+ * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host
+ by moving accept callback function pointer to TCP_PCB_COMMON
+
+ 2009-02-12 Simon Goldschmidt
+ * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size"
+ option)
+
+ 2009-02-11 Simon Goldschmidt
+ * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start)
+
+ 2009-02-11 Simon Goldschmidt
+ * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize:
+ RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv())
+
+ 2009-02-10 Simon Goldschmidt
+ * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD:
+ Accepts_pending is decrease on a corresponding listen pcb when a connection
+ in state SYN_RCVD is close.
+
+ 2009-01-28 Jonathan Larmour
+ * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run
+ out of pool pbufs.
+
+ 2008-12-19 Simon Goldschmidt
+ * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2
+
+ 2008-12-10 Tamas Somogyi, Frédéric Bernon
+ * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and
+ port uses deleted netbuf.
+
+ 2008-10-18 Simon Goldschmidt
+ * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length
+ in tcp_parseopt
+
+ 2008-10-15 Simon Goldschmidt
+ * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers
+ by packing the struct ip_reass_helper.
+
+ 2008-10-03 David Woodhouse, Jonathan Larmour
+ * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address.
+
+ 2008-10-02 Jonathan Larmour
+ * dns.c: Hard-code structure sizes, to avoid issues on some compilers where
+ padding is included.
+
+ 2008-09-30 Jonathan Larmour
+ * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an
+ assertion check that addrlen isn't NULL.
+
+ 2008-09-30 Jonathan Larmour
+ * tcp.c: Fix bug #24227, wrong error message in tcp_bind.
+
+ 2008-08-26 Simon Goldschmidt
+ * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and
+ inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h
+
+ 2008-08-14 Simon Goldschmidt
+ * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when
+ tcp_close returns != ERR_OK)
+
+ 2008-07-08 Frédéric Bernon
+ * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters
+ in macros, mainly if MEM_STATS=0 and MEMP_STATS=0).
+
+ 2008-06-24 Jonathan Larmour
+ * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused
+ if tcp_seg_copy fails.
+
+ 2008-06-17 Simon Goldschmidt
+ * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations)
+ and created defines for swapping bytes and folding u32 to u16.
+
+ 2008-05-30 Kieran Mansley
+ * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd
+ rather than rcv_ann_wnd when deciding if packets are in-window.
+ Contributed by <arasmussen@consultant.datasys.swri.edu>
+
+ 2008-05-30 Kieran Mansley
+ * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow
+ passing as function pointers when MEM_LIBC_MALLOC is defined.
+
+ 2008-05-09 Jonathan Larmour
+ * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to
+ stop it being treated as a fatal error.
+
+ 2008-04-15 Simon Goldschmidt
+ * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP
+ (flag now cleared)
+
+ 2008-03-27 Simon Goldschmidt
+ * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free
+ from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1
+ in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs
+ or heap memory from interrupt context
+
+ 2008-03-26 Simon Goldschmidt
+ * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote
+ host sent a zero mss as TCP option.
+
+
+(STABLE-1.3.0)
+
+ ++ New features:
+
+ 2008-03-10 Jonathan Larmour
+ * inet_chksum.c: Allow choice of one of the sample algorithms to be
+ made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM.
+
+ 2008-01-22 Frédéric Bernon
+ * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in
+ TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names.
+
+ 2008-01-14 Frédéric Bernon
+ * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable
+ to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the
+ tcp_recv callback (see rawapi.txt).
+
+ 2008-01-14 Frédéric Bernon, Marc Chaland
+ * ip.c: Integrate patch #6369" ip_input : checking before realloc".
+
+ 2008-01-12 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::sem per netconn::op_completed like suggested for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-12 Frédéric Bernon
+ * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE,
+ DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues
+ sizes), like suggested for the task #7490 "Add return value to sys_mbox_post".
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490
+ "Add return value to sys_mbox_post". tcpip_callback is always defined as
+ "blocking" ("block" parameter = 1).
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-05 Frédéric Bernon
+ * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h:
+ Introduce changes for task #7490 "Add return value to sys_mbox_post" with some
+ modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which
+ indicate the number of pointers query by the mailbox. There is three defines
+ in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the
+ netconn::acceptmbox. Port maintainers, you can decide to just add this new
+ parameter in your implementation, but to ignore it to keep the previous behavior.
+ The new sys_mbox_trypost function return a value to know if the mailbox is
+ full or if the message is posted. Take a look to sys_arch.txt for more details.
+ This new function is used in tcpip_input (so, can be called in an interrupt
+ context since the function is not blocking), and in recv_udp and recv_raw.
+
+ 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c,
+ tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the
+ "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add
+ documentation in the rawapi.txt file.
+
+ 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer
+
+ 2007-12-31 Frédéric Bernon, Luca Ceresoli
+ * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets
+ in autoip". The change in etharp_raw could be removed, since all calls to
+ etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be
+ wrong in the future.
+
+ 2007-12-30 Frédéric Bernon, Tom Evans
+ * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address
+ Filtering" reported by Tom Evans.
+
+ 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c,
+ sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API
+ applications have to call 'tcp_accepted(pcb)' in their accept callback to
+ keep accepting new connections.
+
+ 2007-12-13 Frédéric Bernon
+ * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result"
+ by err_t type. Add a new err_t code "ERR_INPROGRESS".
+
+ 2007-12-12 Frédéric Bernon
+ * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles
+ are the one which have ram usage.
+
+ 2007-12-05 Frédéric Bernon
+ * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static
+ set of variables (=0) or a local one (=1). In this last case, your port should
+ provide a function "struct hostent* sys_thread_hostent( struct hostent* h)"
+ which have to do a copy of "h" and return a pointer ont the "per-thread" copy.
+
+ 2007-12-03 Simon Goldschmidt
+ * ip.c: ip_input: check if a packet is for inp first before checking all other
+ netifs on netif_list (speeds up packet receiving in most cases)
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access
+ UDP: move a (connected) pcb selected for input to the front of the list of
+ pcbs so that it is found faster next time. Same for RAW pcbs that have eaten
+ a packet.
+
+ 2007-11-28 Simon Goldschmidt
+ * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS
+
+ 2007-11-25 Simon Goldschmidt
+ * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy
+ algorithm.
+
+ 2007-11-24 Simon Goldschmidt
+ * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c
+ to the new file netdb.c; included lwip_getaddrinfo.
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss
+ based on the MTU of the netif used to send. Enabled by default. Disable by
+ setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492.
+
+ 2007-11-19 Frédéric Bernon
+ * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name
+ received match the name query), implement DNS_USES_STATIC_BUF (the place where
+ copy dns payload to parse the response), return an error if there is no place
+ for a new query, and fix some minor problems.
+
+ 2007-11-16 Simon Goldschmidt
+ * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c
+ removed files: core/inet.c, core/inet6.c
+ Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into
+ inet and chksum part; changed includes in all lwIP files as appropriate
+
+ 2007-11-16 Simon Goldschmidt
+ * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential
+ dns resolver function for netconn api (netconn_gethostbyname) and socket api
+ (gethostbyname/gethostbyname_r).
+
+ 2007-11-15 Jim Pettinato, Frédéric Bernon
+ * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
+ requests with RAW api interface. Initialization is done in lwip_init() with
+ build time options. DNS timer is added in tcpip_thread context. DHCP can set
+ DNS server ip addresses when options are received. You need to set LWIP_DNS=1
+ in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
+ some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
+ list with points to improve.
+
+ 2007-11-06 Simon Goldschmidt
+ * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
+ enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
+ for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined.
+
+ 2007-11-06 Simon Goldschmidt
+ * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include
+ core header files in api.h (ip/tcp/udp/raw.h) to hide the internal
+ implementation from netconn api applications.
+
+ 2007-11-03 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP &
+ RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled
+ by default). Netconn API users can use the netconn_recv_bufsize macro to access
+ it. This is a first release which have to be improve for TCP. Note it used the
+ netconn::recv_avail which need to be more "thread-safe" (note there is already
+ the problem for FIONREAD with lwip_ioctl/ioctlsocket).
+
+ 2007-11-01 Frédéric Bernon, Marc Chaland
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c:
+ Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api
+ layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api
+ layer. This option enable to delayed TCP PUSH flag on multiple "write" calls.
+ Note that previous "copy" parameter for "write" APIs is now called "apiflags".
+
+ 2007-10-24 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than
+ TCP_EVENT_xxx macros to get a code more readable. It could also help to remove
+ some code (like we have talk in "patch #5919 : Create compile switch to remove
+ select code"), but it could be done later.
+
+ 2007-10-08 Simon Goldschmidt
+ * many files: Changed initialization: many init functions are not needed any
+ more since we now rely on the compiler initializing global and static
+ variables to zero!
+
+ 2007-10-06 Simon Goldschmidt
+ * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY
+ to enqueue the received pbufs so that multiple packets can be reassembled
+ simultaneously and no static reassembly buffer is needed.
+
+ 2007-10-05 Simon Goldschmidt
+ * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so
+ all netifs (or ports) can use it.
+
+ 2007-10-05 Frédéric Bernon
+ * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the
+ common function to reduce a little bit the footprint (for all functions using
+ only the "netif" parameter).
+
+ 2007-10-03 Frédéric Bernon
+ * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down,
+ netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce
+ a little bit the footprint (for all functions using only the "netif" parameter).
+
+ 2007-09-15 Frédéric Bernon
+ * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF
+ option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for
+ netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for
+ IP_MULTICAST_TTL and IP_MULTICAST_IF.
+
+ 2007-09-10 Frédéric Bernon
+ * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles
+ even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime()
+ each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can
+ decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but
+ call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime()
+ or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+ This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+ snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+ when it's queried (any direct call to "sysuptime" is changed by a call to
+ snmp_get_sysuptime).
+
+ 2007-09-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP,
+ and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags
+ if you want IGMP on an interface. igmp_stop() is now called inside netif_remove().
+ igmp_report_groups() is now called inside netif_set_link_up() (need to have
+ LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait
+ the next query message to receive the matching multicast streams).
+
+ 2007-09-08 Frédéric Bernon
+ * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains
+ IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change).
+ Use this new field to access to common pcb fields (ttl, tos, so_options, etc...).
+ Enable to access to these fields with LWIP_TCP=0.
+
+ 2007-09-05 Frédéric Bernon
+ * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h,
+ ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option
+ LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default).
+ Be careful, disabling ICMP make your product non-compliant to RFC1122, but
+ help to reduce footprint, and to reduce "visibility" on the Internet.
+
+ 2007-09-05 Frédéric Bernon, Bill Florac
+ * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list
+ for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new
+ parameters have to be provided: a task name, and a task stack size. For this
+ one, since it's platform dependant, you could define the best one for you in
+ your lwipopts.h. For port maintainers, you can just add these new parameters
+ in your sys_arch.c file, and but it's not mandatory, use them in your OS
+ specific functions.
+
+ 2007-09-05 Frédéric Bernon
+ * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings
+ inside init.c for task #7142 "Sanity check user-configurable values".
+
+ 2007-09-04 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by
+ memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the
+ value). It will avoid potential fragmentation problems, use a counter to know
+ how many times a group is used on an netif, and free it when all applications
+ leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity
+ check if LWIP_IGMP!=0).
+
+ 2007-09-03 Frédéric Bernon
+ * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement".
+ Initialize igmp_mac_filter to NULL in netif_add (this field should be set in
+ the netif's "init" function). Use the "imr_interface" field (for socket layer)
+ and/or the "interface" field (for netconn layer), for join/leave operations.
+ The igmp_join/leavegroup first parameter change from a netif to an ipaddr.
+ This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany).
+
+ 2007-08-30 Frédéric Bernon
+ * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions
+ from api/api_lib". Now netbuf API is independant of netconn, and can be used
+ with other API (application based on raw API, or future "socket2" API). Ports
+ maintainers just have to add src/api/netbuf.c in their makefile/projects.
+
+ 2007-08-30 Frédéric Bernon, Jonathan Larmour
+ * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check
+ user-configurable values".
+
+ 2007-08-29 Frédéric Bernon
+ * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start.
+ igmp_start is call inside netif_add. Now, igmp initialization is in the same
+ spirit than the others modules. Modify some IGMP debug traces.
+
+ 2007-08-29 Frédéric Bernon
+ * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function"
+ Add lwip_init function to regroup all modules initializations, and to provide
+ a place to add code for task #7142 "Sanity check user-configurable values".
+ Ports maintainers should remove direct initializations calls from their code,
+ and add init.c in their makefiles. Note that lwip_init() function is called
+ inside tcpip_init, but can also be used by raw api users since all calls are
+ disabled when matching options are disabled. Also note that their is new options
+ in opt.h, you should configure in your lwipopts.h (they are enabled per default).
+
+ 2007-08-26 Marc Boucher
+ * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL
+ since they can under certain circumstances be called with an invalid conn
+ pointer after the connection has been closed (and conn has been freed).
+
+ 2007-08-25 Frédéric Bernon (Artem Migaev's Patch)
+ * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up".
+ Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set.
+
+ 2007-08-22 Frédéric Bernon
+ * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK
+ to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release.
+
+ 2007-08-22 Frédéric Bernon
+ * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT &
+ ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the
+ name is tcpip_input (we keep the name of 1.2.0 function).
+
+ 2007-08-17 Jared Grubb
+ * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool
+ settings into new memp_std.h and optional user file lwippools.h. This adds
+ more dynamic mempools, and allows the user to create an arbitrary number of
+ mempools for mem_malloc.
+
+ 2007-08-16 Marc Boucher
+ * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function;
+ otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely
+ close the connection.
+
+ 2007-08-16 Marc Boucher
+ * sockets.c: lwip_accept(): check netconn_peer() error return.
+
+ 2007-08-16 Marc Boucher
+ * mem.c, mem.h: Added mem_calloc().
+
+ 2007-08-16 Marc Boucher
+ * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT)
+ for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG
+ and starving other message types.
+ Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API
+
+ 2007-08-16 Marc Boucher
+ * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf
+ type and flgs (later renamed to flags).
+ Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*.
+ Improved lwip_recvfrom(). TCP push now propagated.
+
+ 2007-08-16 Marc Boucher
+ * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global
+ provided by etharp.
+
+ 2007-08-16 Marc Boucher
+ * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h,
+ etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c:
+ Added PPPoE support and various PPP improvements.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial,
+ making netbuf_copy_partial use this function.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with
+ 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and
+ other stacks.
+
+ 2007-07-13 Jared Grubb (integrated by Frédéric Bernon)
+ * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add
+ a link callback in the netif struct, and functions to handle it. Be carefull
+ for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c)
+ if you want to be sure to be compatible with future changes...
+
+ 2007-06-30 Frédéric Bernon
+ * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
+
+ 2007-06-21 Simon Goldschmidt
+ * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both
+ LWIP_AUTOIP =0 and =1 to remove redundant code.
+
+ 2007-06-21 Simon Goldschmidt
+ * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option
+ MEM_USE_POOLS to use 4 pools with different sized elements instead of a
+ heap. This both prevents memory fragmentation and gives a higher speed
+ at the cost of more memory consumption. Turned off by default.
+
+ 2007-06-21 Simon Goldschmidt
+ * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of
+ netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into
+ int to be able to send a bigger buffer than 64K with one time (mainly
+ used from lwip_send).
+
+ 2007-06-21 Simon Goldschmidt
+ * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write
+ into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too.
+
+ 2007-06-21 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in
+ netconn_write from api_lib.c to api_msg.c to also prevent multiple context-
+ changes on low memory or empty send-buffer.
+
+ 2007-06-18 Simon Goldschmidt
+ * etharp.c, etharp.h: Changed etharp to use a defined hardware address length
+ of 6 to avoid loading netif->hwaddr_len every time (since this file is only
+ used for ethernet and struct eth_addr already had a defined length of 6).
+
+ 2007-06-17 Simon Goldschmidt
+ * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets
+ to disable UDP checksum generation on transmit.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid
+ pointers or parameters, and let the possibility to redefined it in cc.h. Use
+ this macro to check "conn" parameter in api_msg.c functions.
+
+ 2007-06-11 Simon Goldschmidt
+ * sockets.c, sockets.h: Added UDP lite support for sockets
+
+ 2007-06-10 Simon Goldschmidt
+ * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled
+ by default) to switch off UDP-Lite support if not needed (reduces udp.c code
+ size)
+
+ 2007-06-09 Dominik Spies (integrated by Frédéric Bernon)
+ * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h:
+ AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and
+ LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt
+ (see TODO mark in the source code).
+
+ 2007-06-09 Simon Goldschmidt
+ * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for
+ etharp_output() to match netif->output so etharp_output() can be used
+ directly as netif->output to save one function call.
+
+ 2007-06-08 Simon Goldschmidt
+ * netif.h, ethernetif.c, slipif.c, loopif.c: Added define
+ NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables,
+ added initialization of those to ethernetif, slipif and loopif.
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF
+ (defaulting to off for now) that can be set to 0 to send fragmented
+ packets by passing PBUF_REFs down the stack.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP
+ connections, such present in patch #5959.
+
+ 2007-05-23 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx
+ code in only one part...
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp
+ elements to overflow. This is achieved by adding some bytes before and after
+ each pool element (increasing their size, of course), filling them with a
+ prominent value and checking them on freeing the element.
+ Set it to 2 to also check every element in every pool each time memp_malloc()
+ or memp_free() is called (slower but more helpful).
+
+ 2007-05-10 Simon Goldschmidt
+ * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for
+ PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
+ code size.
+
+ 2007-05-11 Frédéric Bernon
+ * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c:
+ Include a function pointer instead of a table index in the message to reduce
+ footprint. Disable some part of lwip_send and lwip_sendto if some options are
+ not set (LWIP_TCP, LWIP_UDP, LWIP_RAW).
+
+ 2007-05-10 Simon Goldschmidt
+ * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus
+ \ extern "C" {' in all header files. Now you can write your application using
+ the lwIP stack in C++ and simply #include the core files. Note I have left
+ out the netif/ppp/*h header files for now, since I don't know which files are
+ included by applications and which are for internal use only.
+
+ 2007-05-09 Simon Goldschmidt
+ * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library
+ memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for
+ situations where some compilers might inline the copy and save a function
+ call. Also replaced all calls to memcpy() with calls to (S)MEMCPY().
+
+ 2007-05-08 Simon Goldschmidt
+ * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc())
+ to be overriden in case the C-library malloc implementation is not protected
+ against concurrent access.
+
+ 2007-05-04 Simon Goldschmidt (Atte Kojo)
+ * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending
+ multiple packets to the same host.
+
+ 2007-05-04 Frédéric Bernon, Jonathan Larmour
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible
+ to corrupt remote addr/port connection state". Reduce problems "not enought memory" with
+ netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between
+ sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function.
+ Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct,
+ these fields are now renamed "addr" & "port".
+
+ 2007-04-11 Jonathan Larmour
+ * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new
+ sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return
+ with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro
+ by the port in sys_arch.h if desired.
+
+ 2007-04-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API
+ allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp
+ clients, using new functions from netifapi.h. Disable as default (no port change to do).
+
+ 2007-04-05 Frédéric Bernon
+ * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant.
+
+ 2007-04-04 Simon Goldschmidt
+ * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x)
+ use this for and architecture-independent form to tell the compiler you intentionally
+ are not using this variable. Can be overriden in cc.h.
+
+ 2007-03-28 Frédéric Bernon
+ * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to
+ define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded
+ string, point on one of your's ethernetif field, or alloc a string you will free yourself).
+ It will be used by DHCP to register a client hostname, but can also be use when you call
+ snmp_set_sysname.
+
+ 2007-03-28 Frédéric Bernon
+ * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to
+ initialize a network interface's flag with. It tell this interface is an ethernet
+ device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility
+ Support for IPv4" section 4.6) when interface is "up" with netif_set_up().
+
+ 2007-03-26 Frédéric Bernon, Jonathan Larmour
+ * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build
+ time if you only use PPP or SLIP. The default is enable. Note we don't have to call
+ etharp_init in your port's initilization sequence if you use tcpip.c, because this call
+ is done in tcpip_init function.
+
+ 2007-03-22 Frédéric Bernon
+ * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the
+ new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in
+ your lwipopts.h. More, unused counters are not defined in the stats structs, and not
+ display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined
+ but never used. Fix msg_in.c with the correct #if test for a stat display.
+
+ 2007-03-21 Kieran Mansley
+ * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com).
+ Provides callback on netif up/down state change.
+
+ 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c,
+ ip.c, netif.h, tcpip.c, opt.h:
+ New configuration option LWIP_IGMP to enable IGMP processing. Based on only one
+ filter per all network interfaces. Declare a new function in netif to enable to
+ control the MAC filter (to reduce lwIP traffic processing).
+
+ 2007-03-11 Frédéric Bernon
+ * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can
+ be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this
+ unless you know what you're doing (default are RFC1122 compliant). Note
+ that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds.
+
+ 2007-03-08 Frédéric Bernon
+ * tcp.h: Keepalive values can be configured at compile time, but don't change
+ this unless you know what you're doing (default are RFC1122 compliant).
+
+ 2007-03-08 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h:
+ Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO
+ on UDP sockets/netconn.
+
+ 2007-03-08 Simon Goldschmidt
+ * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time.
+
+ 2007-03-06 Frédéric Bernon
+ * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h:
+ Implement SO_RCVTIMEO on UDP sockets/netconn.
+
+ 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt)
+ * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated
+ on the stack and remove the API msg type from memp
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * sockets.h, sockets.c: Move socket initialization to new
+ lwip_socket_init() function.
+ NOTE: this changes the API with ports. Ports will have to be
+ updated to call lwip_socket_init() now.
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * api_lib.c: Use memcpy in netbuf_copy_partial.
+
+
+ ++ Bug fixes:
+
+ 2008-03-17 Frédéric Bernon, Ed Kerekes
+ * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have
+ some problems to fill the IP header on some targets, use now the
+ ip.h macros to do it).
+
+ 2008-03-13 Frédéric Bernon
+ * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using
+ (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a
+ TCP connection caused a crash. Note that using (lwip_)recvfrom
+ like this is a bit slow and that using (lwip)getpeername is the
+ good lwip way to do it (so, using recv is faster on tcp sockets).
+
+ 2008-03-12 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's
+ recv_raw() does not consume data", and the ping sample (with
+ LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom
+ returned the IP payload, without the IP header).
+
+ 2008-03-04 Jonathan Larmour
+ * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors
+ and/or warnings on some systems where mem_size_t and size_t differ.
+ * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc.
+
+ 2008-03-04 Kieran Mansley (contributions by others)
+ * Numerous small compiler error/warning fixes from contributions to
+ mailing list after 1.3.0 release candidate made.
+
+ 2008-01-25 Cui hengbin (integrated by Frédéric Bernon)
+ * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures.
+
+ 2008-01-15 Kieran Mansley
+ * tcp_out.c: BUG20511. Modify persist timer to start when we are
+ prevented from sending by a small send window, not just a zero
+ send window.
+
+ 2008-01-09 Jonathan Larmour
+ * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid
+ conflict with Linux system headers.
+
+ 2008-01-06 Jonathan Larmour
+ * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP
+ address entirely on receiving a DHCPNAK, and restarting discovery.
+
+ 2007-12-21 Simon Goldschmidt
+ * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail
+ is not protected" by using new macros for interlocked access to modify/test
+ netconn->recv_avail.
+
+ 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev)
+ * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state)
+
+ 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling
+ of silly window avoidance and prevent lwIP from shrinking the window)
+
+ 2007-12-04 Simon Goldschmidt
+ * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last
+ data packet was lost): add assert that all segment lists are empty in
+ tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED
+ state from LAST_ACK in tcp_process
+
+ 2007-12-02 Simon Goldschmidt
+ * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET
+ If including <sys/time.h> for system-struct timeval, LWIP_TIMEVAL_PRIVATE now
+ has to be set to 0 in lwipopts.h
+
+ 2007-12-02 Simon Goldschmidt
+ * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always
+ allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen
+ netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox.
+ This is a fix for thread-safety and allocates all items needed for a netconn
+ when the netconn is created.
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple
+ netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed
+ to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same
+ port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address)
+
+ 2007-11-27 Simon Goldschmidt
+ * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by
+ letting ip_route only use netifs that are up.
+
+ 2007-11-27 Simon Goldschmidt
+ * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF
+ and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and
+ sockets block most operations once they have seen a fatal error.
+
+ 2007-11-27 Simon Goldschmidt
+ * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the
+ netif to send as an argument (to be able to send on netifs that are down).
+
+ 2007-11-26 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs
+ arrive out-of-order
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early
+ Fixed the nagle algorithm; nagle now also works for all raw API applications
+ and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY'
+
+ 2007-11-12 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most
+ of the netconn_peer and netconn_addr processing is done inside tcpip_thread
+ context in do_getaddr.
+
+ 2007-11-10 Simon Goldschmidt
+ * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can
+ happen any time). Now the packet simply isn't enqueued when out of memory.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or
+ TCP_MSS if that is smaller) as long as no MSS option is received from the
+ remote host.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN)
+ is now based on TCP_MSS instead of pcb->mss (on passive open now effectively
+ sending our configured TCP_MSS instead of the one received).
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was
+ calculated based on the configured TCP_MSS, not on the MSS option received
+ with SYN+ACK.
+
+ 2007-10-09 Simon Goldschmidt
+ * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too
+ short and also was generated wrong if checksum coverage != tot_len;
+ receive: checksum was calculated wrong if checksum coverage != tot_len
+
+ 2007-10-08 Simon Goldschmidt
+ * mem.c: lfree was not updated in mem_realloc!
+
+ 2007-10-07 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
+ crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:
+ this change cause an API breakage for netconn_addr, since a parameter
+ type change. Any compiler should cause an error without any changes in
+ yours netconn_peer calls (so, it can't be a "silent change"). It also
+ reduce a little bit the footprint for socket layer (lwip_getpeername &
+ lwip_getsockname use now a common lwip_getaddrname function since
+ netconn_peer & netconn_addr have the same parameters).
+
+ 2007-09-20 Simon Goldschmidt
+ * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state)
+ by checking tcp_tw_pcbs also
+
+ 2007-09-19 Simon Goldschmidt
+ * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies)
+
+ 2007-09-15 Mike Kleshov
+ * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used)
+
+ 2007-09-06 Frédéric Bernon
+ * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove
+ it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which
+ already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h"
+ if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h.
+
+ 2007-08-30 Frédéric Bernon
+ * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces,
+ and fix some coding style.
+
+ 2007-08-28 Frédéric Bernon
+ * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any
+ kind of packets. These packets are considered like Ethernet packets (payload
+ pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets
+ are considered like IP packets (payload pointing to iphdr).
+
+ 2007-08-27 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error
+ problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state
+ and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT).
+
+ 2007-08-24 Kieran Mansley
+ * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy
+ compiler (Paradigm C++)
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement.
+ Introduce IGMP_STATS to centralize statistics management.
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast
+ packet on a udp pcb binded on an netif's IP address, and not on "any".
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement.
+ This is mainly on using lookup/lookfor, and some coding styles...
+
+ 2007-07-26 Frédéric Bernon (and "thedoctor")
+ * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
+ tcp_output fails in tcp_close, the code in do_close_internal gets simpler
+ (tcp_output is called again later from tcp timers).
+
+ 2007-07-25 Simon Goldschmidt
+ * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
+ copy_from_pbuf, which illegally modified the given pbuf.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs:
+ changed snd_queuelen++ to snd_queuelen += pbuf_clen(p).
+
+ 2007-07-24 Simon Goldschmidt
+ * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the
+ correct state (must be CLOSED).
+
+ 2007-07-13 Thomas Taranowski (commited by Jared Grubb)
+ * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed
+ allocation. It now returns NULL.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in
+ all error cases.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed,
+ because current code doesn't follow rawapi.txt documentation.
+
+ 2007-07-13 Kieran Mansley
+ * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in
+ out of sequence processing of received packets
+
+ 2007-07-03 Simon Goldschmidt
+ * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an
+ assumption is made that this pbuf is in one piece (i.e. not chained). These
+ assumptions clash with the possibility of converting to fully pool-based
+ pbuf implementations, where PBUF_RAM pbufs might be chained.
+
+ 2007-07-03 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems
+ when closing tcp netconns: removed conn->sem, less context switches when
+ closing, both netconn_close and netconn_delete should safely close tcp
+ connections.
+
+ 2007-07-02 Simon Goldschmidt
+ * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c,
+ tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off)
+ to cache ARP table indices with each pcb instead of single-entry cache for
+ the complete stack.
+
+ 2007-07-02 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent
+ warnings when assigning to smaller types.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if
+ a segment contained chained pbufs)
+
+ 2007-06-28 Frédéric Bernon
+ * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute
+ a "pseudo-random" value based on netif's MAC and some autoip fields. It's always
+ possible to define this macro in your own lwipopts.h to always use C library's
+ rand(). Note that autoip_create_rand_addr doesn't use this macro.
+
+ 2007-06-28 Frédéric Bernon
+ * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option
+ LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications
+ in api_lib/api_msg (use pointers and not type with table, etc...)
+
+ 2007-06-26 Simon Goldschmidt
+ * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload
+ for udp packets with no matching pcb.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match
+ could get udp input packets if the remote side matched.
+
+ 2007-06-13 Simon Goldschmidt
+ * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get
+ changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0.
+
+ 2007-06-13 Simon Goldschmidt
+ * api_msg.c: pcb_new sets conn->err if protocol is not implemented
+ -> netconn_new_..() does not allocate a new connection for unsupported
+ protocols.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * api_lib.c: change return expression in netconn_addr and netconn_peer, because
+ conn->err was reset to ERR_OK without any reasons (and error was lost)...
+
+ 2007-06-13 Frédéric Bernon, Matthias Weisser
+ * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename
+ MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid
+ some macro names collision with some OS macros.
+
+ 2007-06-11 Simon Goldschmidt
+ * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0,
+ create checksum over the complete packet. On RX, if it's < 8 (and not 0),
+ discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both
+ UDP & UDP Lite.
+
+ 2007-06-11 Srinivas Gollakota & Oleg Tyshev
+ * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags"
+ where TCP flags wasn't initialized in tcp_keepalive.
+
+ 2007-06-03 Simon Goldschmidt
+ * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function
+ registered, p->payload was modified without modifying p->len if sending
+ icmp_dest_unreach() (had no negative effect but was definitively wrong).
+
+ 2007-06-03 Simon Goldschmidt
+ * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp
+ re-used the input pbuf even if that didn't have enough space to include the
+ link headers. Now the space is tested and a new pbuf is allocated for the
+ echo response packet if the echo request pbuf isn't big enough.
+
+ 2007-06-01 Simon Goldschmidt
+ * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only
+ allocated by do_listen if success) and netconn_accept errors handling. In
+ most of api_lib functions, we replace some errors checkings like "if (conn==NULL)"
+ by ASSERT, except for netconn_delete.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return
+ an error code if it's impossible to fetch a pbuf on a TCP connection (and not
+ directly close the recvmbox).
+
+ 2007-05-22 Simon Goldschmidt
+ * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of
+ bound but unconnected (and non-listening) tcp_pcbs.
+
+ 2007-05-22 Frédéric Bernon
+ * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only
+ used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of
+ sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features
+ like "sys_timeout" in their application threads.
+
+ 2007-05-22 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see
+ which parameters are used by which do_xxx function, and to avoid "misusing"
+ parameters (patch #5938).
+
+ 2007-05-22 Simon Goldschmidt
+ * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938:
+ changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto
+ is only 8 bits wide. This affects the api, as there, the protocol was
+ u16_t, too.
+
+ 2007-05-18 Simon Goldschmidt
+ * memp.c: addition to patch #5913: smaller pointer was returned but
+ memp_memory was the same size -> did not save memory.
+
+ 2007-05-16 Simon Goldschmidt
+ * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns
+ != ERR_OK.
+
+ 2007-05-16 Simon Goldschmidt
+ * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same
+ as the one of the netif used for sending to prevent sending from old
+ addresses after a netif address gets changed (partly fixes bug #3168).
+
+ 2007-05-16 Frédéric Bernon
+ * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work
+ with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in
+ tcpip_init) because we have to be sure that network interfaces are already
+ added (mac filter is updated only in igmp_init for the moment).
+
+ 2007-05-16 Simon Goldschmidt
+ * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls
+ into sys_arch_sem_wait calls to prevent timers from running while waiting
+ for the heap. This fixes bug #19167.
+
+ 2007-05-13 Simon Goldschmidt
+ * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines
+ for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from
+ tcp.h to sockets.h.
+
+ 2007-05-07 Simon Goldschmidt
+ * mem.c: Another attempt to fix bug #17922.
+
+ 2007-05-04 Simon Goldschmidt
+ * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy()
+ implementation so that it can be reused (don't allocate the target
+ pbuf inside pbuf_copy()).
+
+ 2007-05-04 Simon Goldschmidt
+ * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem
+ to save a little RAM (next pointer of memp is not used while not in pool).
+
+ 2007-05-03 "maq"
+ * sockets.c: Fix ioctl FIONREAD when some data remains from last recv.
+ (patch #3574).
+
+ 2007-04-23 Simon Goldschmidt
+ * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results
+ in NULL reference for incoming TCP packets". Loopif has to be configured
+ (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input()
+ (multithreading environments, e.g. netif->input() = tcpip_input()) or
+ putting packets on a list that is fed to the stack by calling loopif_poll()
+ (single-thread / NO_SYS / polling environment where e.g.
+ netif->input() = ip_input).
+
+ 2007-04-17 Jonathan Larmour
+ * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold
+ the difference between two u16_t's.
+ * sockets.h: FD_SETSIZE needs to match number of sockets, which is
+ MEMP_NUM_NETCONN in sockets.c right now.
+
+ 2007-04-12 Jonathan Larmour
+ * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580).
+
+ 2007-04-12 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission
+ timer is reset to fix bug#19434, with help from Oleg Tyshev.
+
+ 2007-04-11 Simon Goldschmidt
+ * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than
+ previously thought need to be copied (everything but PBUF_ROM!). Cleaned up
+ pbuf.c: removed functions no needed any more (by etharp).
+
+ 2007-04-11 Kieran Mansley
+ * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix
+ "Constant is long" warnings with 16bit compilers. Contributed by
+ avatar@mmlab.cse.yzu.edu.tw
+
+ 2007-04-05 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on
+ the mailbox is active". Now, the post is only done during a connect, and do_send,
+ do_write and do_join_leave_group don't do anything if a previous error was signaled.
+
+ 2007-04-03 Frédéric Bernon
+ * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output
+ packets. See patch #5834.
+
+ 2007-03-30 Frédéric Bernon
+ * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add
+ missing pcb allocations checking (in do_bind, and for each raw_new). Fix style.
+
+ 2007-03-30 Frédéric Bernon
+ * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with
+ others environment defines (these were too "generic").
+
+ 2007-03-28 Frédéric Bernon
+ * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call
+ result and can cause a crash. lwip_send now check netbuf_ref result.
+
+ 2007-03-28 Simon Goldschmidt
+ * sockets.c Remove "#include <errno.h>" from sockets.c to avoid multiple
+ definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is
+ defined. This is the way it should have been already (looking at
+ doc/sys_arch.txt)
+
+ 2007-03-28 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS +
+ IP and TCP headers *and* physical link headers
+
+ 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause
+ to send some garbage. It is not a definitive solution, but the patch does solve
+ the problem for most cases.
+
+ 2007-03-22 Frédéric Bernon
+ * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used).
+
+ 2007-03-22 Frédéric Bernon
+ * api_lib.c: somes resources couldn't be freed if there was errors during
+ netconn_new_with_proto_and_callback.
+
+ 2007-03-22 Frédéric Bernon
+ * ethernetif.c: update netif->input calls to check return value. In older ports,
+ it's a good idea to upgrade them, even if before, there could be another problem
+ (access to an uninitialized mailbox).
+
+ 2007-03-21 Simon Goldschmidt
+ * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
+ by casting to unsigned).
+
+ 2007-03-21 Frédéric Bernon
+ * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from
+ api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a
+ dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call.
+ Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a
+ faster and more reliable communication between api_lib and tcpip.
+
+ 2007-03-21 Frédéric Bernon
+ * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0.
+
+ 2007-03-21 Frédéric Bernon
+ * api_msg.c, igmp.c, igmp.h: Fix C++ style comments
+
+ 2007-03-21 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS +
+ IP and TCP headers
+
+ 2007-03-21 Kieran Mansley
+ * Fix all uses of pbuf_header to check the return value. In some
+ cases just assert if it fails as I'm not sure how to fix them, but
+ this is no worse than before when they would carry on regardless
+ of the failure.
+
+ 2007-03-21 Kieran Mansley
+ * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and
+ comment out missing header include in icmp.c
+
+ 2007-03-20 Frédéric Bernon
+ * memp.h, stats.c: Fix stats_display function where memp_names table wasn't
+ synchronized with memp.h.
+
+ 2007-03-20 Frédéric Bernon
+ * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input,
+ tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with
+ network interfaces. Also fix a compiler warning.
+
+ 2007-03-20 Kieran Mansley
+ * udp.c: Only try and use pbuf_header() to make space for headers if
+ not a ROM or REF pbuf.
+
+ 2007-03-19 Frédéric Bernon
+ * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg()
+ and api_msg_post().
+
+ 2007-03-19 Frédéric Bernon
+ * Remove unimplemented "memp_realloc" function from memp.h.
+
+ 2007-03-11 Simon Goldschmidt
+ * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused
+ memory corruption.
+
+ 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251
+ (missing `const' qualifier in socket functions), to get more compatible to
+ standard POSIX sockets.
+
+ 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * sockets.c: Add asserts inside bind, connect and sendto to check input
+ parameters. Remove excessive set_errno() calls after get_socket(), because
+ errno is set inside of get_socket(). Move last sock_set_errno() inside
+ lwip_close.
+
+ 2007-03-09 Simon Goldschmidt
+ * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory
+ was allocated too small.
+
+ 2007-03-06 Simon Goldschmidt
+ * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect
+ the stack from concurrent access.
+
+ 2007-03-06 Frédéric Bernon, Dmitry Potapov
+ * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy
+ call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input().
+
+ 2007-03-06 Simon Goldschmidt
+ * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files
+ if IP_FRAG == 0 and IP_REASSEMBLY == 0
+
+ 2007-03-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration
+ option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput.
+ Allow to do ARP processing for incoming packets inside tcpip_thread
+ (protecting ARP layer against concurrent access). You can also disable
+ old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0.
+ Older ports have to use tcpip_ethinput.
+
+ 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * err.h, err.c: fixed compiler warning "initialization dircards qualifiers
+ from pointer target type"
+
+ 2007-03-05 Frédéric Bernon
+ * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES,
+ ETHARP_TRUST_IP_MAC, review SO_REUSE)
+
+ 2007-03-04 Frédéric Bernon
+ * api_msg.c: Remove some compiler warnings : parameter "pcb" was never
+ referenced.
+
+ 2007-03-04 Frédéric Bernon
+ * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from
+ Dmitry Potapov).
+ The api_msg struct stay on the stack (not moved to netconn struct).
+
+ 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if
+ SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available)
+ Also fixed cast warning in pbuf_alloc()
+
+ 2007-03-04 Simon Goldschmidt
+ * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt
+ existing pbuf chain when enqueuing multiple pbufs to a pending ARP request
+
+ 2007-03-03 Frédéric Bernon
+ * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;"
+ It is static, and never used in udp.c except udp_init().
+
+ 2007-03-02 Simon Goldschmidt
+ * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from
+ tcpip_thread() to tcpip_init(). This way, raw API connections can be
+ initialized before tcpip_thread is running (e.g. before OS is started)
+
+ 2007-03-02 Frédéric Bernon
+ * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
+ interval.
+
+ 2007-02-28 Kieran Mansley
+ * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved
+ outside the region of the pbuf by pbuf_header()
+
+ 2007-02-28 Kieran Mansley
+ * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero
+ when supplied timeout is also non-zero
+
+(STABLE-1.2.0)
+
+ 2006-12-05 Leon Woestenberg
+ * CHANGELOG: Mention STABLE-1.2.0 release.
+
+ ++ New features:
+
+ 2006-12-01 Christiaan Simons
+ * mem.h, opt.h: Added MEM_LIBC_MALLOC option.
+ Note this is a workaround. Currently I have no other options left.
+
+ 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour)
+ * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define
+ to include/lwip/opt.h.
+ * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL.
+ Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h.
+ * opt.h: Add above new options.
+
+ 2006-08-18 Christiaan Simons
+ * tcp_{in,out}.c: added SNMP counters.
+ * ipv4/ip.c: added SNMP counters.
+ * ipv4/ip_frag.c: added SNMP counters.
+
+ 2006-08-08 Christiaan Simons
+ * etharp.{c,h}: added etharp_find_addr() to read
+ (stable) ethernet/IP address pair from ARP table
+
+ 2006-07-14 Christiaan Simons
+ * mib_structs.c: added
+ * include/lwip/snmp_structs.h: added
+ * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct
+
+ 2006-07-06 Christiaan Simons
+ * snmp/asn1_{enc,dec}.c added
+ * snmp/mib2.c added
+ * snmp/msg_{in,out}.c added
+ * include/lwip/snmp_asn1.h added
+ * include/lwip/snmp_msg.h added
+ * doc/snmp_agent.txt added
+
+ 2006-03-29 Christiaan Simons
+ * inet.c, inet.h: Added platform byteswap support.
+ Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and
+ optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros.
+
+ ++ Bug fixes:
+
+ 2006-11-30 Christiaan Simons
+ * dhcp.c: Fixed false triggers of request_timeout.
+
+ 2006-11-28 Christiaan Simons
+ * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags.
+
+ 2006-10-11 Christiaan Simons
+ * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h:
+ Partially accepted patch #5449 for ANSI C compatibility / build fixes.
+ * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol
+ identifier from 170 to 136 (bug #17574).
+
+ 2006-10-10 Christiaan Simons
+ * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice.
+
+ 2006-08-17 Christiaan Simons
+ * udp.c: Fixed bug #17200, added check for broadcast
+ destinations for PCBs bound to a unicast address.
+
+ 2006-08-07 Christiaan Simons
+ * api_msg.c: Flushing TCP output in do_close() (bug #15926).
+
+ 2006-06-27 Christiaan Simons
+ * api_msg.c: Applied patch for cold case (bug #11135).
+ In accept_function() ensure newconn->callback is always initialized.
+
+ 2006-06-15 Christiaan Simons
+ * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748),
+ facilitate printing of mem_size_t and u16_t statistics.
+
+ 2006-06-14 Christiaan Simons
+ * api_msg.c: Applied patch #5146 to handle allocation failures
+ in accept() by Kevin Lawson.
+
+ 2006-05-26 Christiaan Simons
+ * api_lib.c: Removed conn->sem creation and destruction
+ from netconn_write() and added sys_sem_new to netconn_new_*.
+
+(STABLE-1_1_1)
+
+ 2006-03-03 Christiaan Simons
+ * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap
+ access and added pbuf_alloc() return value checks.
+
+ 2006-01-01 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is
+ now handled by the checksum routine properly.
+
+ 2006-02-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fix alignment; pbuf_init() would not work unless
+ pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.)
+
+ 2005-12-20 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch
+ submitted by Mitrani Hiroshi.
+
+ 2005-12-15 Christiaan Simons
+ * inet.c: Disabled the added summing routine to preserve code space.
+
+ 2005-12-14 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson.
+ Added Curt McDowell's optimized checksumming routine for future
+ inclusion. Need to create test case for unaliged, aligned, odd,
+ even length combination of cases on various endianess machines.
+
+ 2005-12-09 Christiaan Simons
+ * inet.c: Rewrote standard checksum routine in proper portable C.
+
+ 2005-11-25 Christiaan Simons
+ * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only.
+ * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t,
+ u32_t, s32_t typedefs. This solves most debug word-length assumes.
+
+ 2005-07-17 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * inet.c: Fixed unaligned 16-bit access in the standard checksum
+ routine by Peter Jolasson.
+ * slipif.c: Fixed implementation assumption of single-pbuf datagrams.
+
+ 2005-02-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch.
+ * tcp_{out|in}.c: Applied patch fixing unaligned access.
+
+ 2005-01-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement.
+
+ 2005-01-03 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * udp.c: UDP pcb->recv() was called even when it was NULL.
+
+(STABLE-1_1_0)
+
+ 2004-12-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Disabled multiple packets on the ARP queue.
+ This clashes with TCP queueing.
+
+ 2004-11-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Fixed race condition from ARP request to ARP timeout.
+ Halved the ARP period, doubled the period counts.
+ ETHARP_MAX_PENDING now should be at least 2. This prevents
+ the counter from reaching 0 right away (which would allow
+ too little time for ARP responses to be received).
+
+ 2004-11-25 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * dhcp.c: Decline messages were not multicast but unicast.
+ * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD.
+ Do not try hard to insert arbitrary packet's source address,
+ etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD.
+ etharp_query() now always DOES call ETHARP_TRY_HARD so that users
+ querying an address will see it appear in the cache (DHCP could
+ suffer from this when a server invalidly gave an in-use address.)
+ * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are
+ comparing network addresses (identifiers), not the network masks
+ themselves.
+ * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given
+ IP address actually belongs to the network of the given interface.
+
+ 2004-11-24 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state.
+
+(STABLE-1_1_0-RC1)
+
+ 2004-10-16 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately,
+ even if one is already pending, if the rcv_wnd is above a threshold
+ (currently TCP_WND/2). This avoids waiting for a timer to expire to send a
+ delayed ACK in order to open the window if the stack is only receiving data.
+
+ 2004-09-12 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp*.*: Retransmit time-out handling improvement by Sam Jansen.
+
+ 2004-08-20 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Make sure the first pbuf queued on an ARP entry
+ is properly ref counted.
+
+ 2004-07-27 Tony Mountifield <tony@softins.co.uk>
+ * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler
+ warnings about comparison.
+ * pbuf.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty. Closed an unclosed comment.
+ * tcp.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty.
+ * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons().
+ * inet.c: Added a couple of casts to quiet the compiler.
+ No need to test isascii(c) before isdigit(c) or isxdigit(c).
+
+ 2004-07-22 Tony Mountifield <tony@softins.co.uk>
+ * inet.c: Made data types consistent in inet_ntoa().
+ Added casts for return values of checksum routines, to pacify compiler.
+ * ip_frag.c, tcp_out.c, sockets.c, pbuf.c
+ Small corrections to some debugging statements, to pacify compiler.
+
+ 2004-07-21 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Removed spurious semicolon and added missing end-of-comment.
+ * ethernetif.c Updated low_level_output() to match prototype for
+ netif->linkoutput and changed low_level_input() similarly for consistency.
+ * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype
+ of raw_recv() in raw.h and so avoid compiler error.
+ * sockets.c: Added trivial (int) cast to keep compiler happier.
+ * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros.
+
+(STABLE-1_0_0)
+
+ ++ Changes:
+
+ 2004-07-05 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure
+ your cc.h file defines this either 1 or 0. If non-defined,
+ defaults to 1.
+ * .c: Added <string.h> and <errno.h> includes where used.
+ * etharp.c: Made some array indices unsigned.
+
+ 2004-06-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * netif.*: Added netif_set_up()/down().
+ * dhcp.c: Changes to restart program flow.
+
+ 2004-05-07 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: In find_entry(), instead of a list traversal per candidate, do a
+ single-pass lookup for different candidates. Should exploit locality.
+
+ 2004-04-29 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp*.c: Cleaned up source comment documentation for Doxygen processing.
+ * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC.
+ * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by
+ the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option.
+
+ ++ Bug fixes:
+
+ 2004-04-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution
+ suggested by Timmy Brolin. Fix for 32-bit processors that cannot access
+ non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix
+ is to prefix the 14-bit Ethernet headers with two padding bytes.
+
+ 2004-04-23 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * ip_addr.c: Fix in the ip_addr_isbroadcast() check.
+ * etharp.c: Fixed the case where the packet that initiates the ARP request
+ is not queued, and gets lost. Fixed the case where the packets destination
+ address is already known; we now always queue the packet and perform an ARP
+ request.
+
+(STABLE-0_7_0)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition.
+ * Fixed TCP bug in dequeueing of FIN from out of order segment queue.
+ * Fixed two possible NULL references in rare cases.
+
+(STABLE-0_6_6)
+
+ ++ Bug fixes:
+
+ * Fixed DHCP which did not include the IP address in DECLINE messages.
+
+ ++ Changes:
+
+ * etharp.c has been hauled over a bit.
+
+(STABLE-0_6_5)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic.
+ * Packets sent from ARP queue had invalid source hardware address.
+
+ ++ Changes:
+
+ * Pass-by ARP requests do now update the cache.
+
+ ++ New features:
+
+ * No longer dependent on ctype.h.
+ * New socket options.
+ * Raw IP pcb support.
+
+(STABLE-0_6_4)
+
+ ++ Bug fixes:
+
+ * Some debug formatters and casts fixed.
+ * Numereous fixes in PPP.
+
+ ++ Changes:
+
+ * DEBUGF now is LWIP_DEBUGF
+ * pbuf_dechain() has been re-enabled.
+ * Mentioned the changed use of CVS branches in README.
+
+(STABLE-0_6_3)
+
+ ++ Bug fixes:
+
+ * Fixed pool pbuf memory leak in pbuf_alloc().
+ Occured if not enough PBUF_POOL pbufs for a packet pbuf chain.
+ Reported by Savin Zlobec.
+
+ * PBUF_POOL chains had their tot_len field not set for non-first
+ pbufs. Fixed in pbuf_alloc().
+
+ ++ New features:
+
+ * Added PPP stack contributed by Marc Boucher
+
+ ++ Changes:
+
+ * Now drops short packets for ICMP/UDP/TCP protocols. More robust.
+
+ * ARP queueuing now queues the latest packet instead of the first.
+ This is the RFC recommended behaviour, but can be overridden in
+ lwipopts.h.
+
+(0.6.2)
+
+ ++ Bugfixes:
+
+ * TCP has been fixed to deal with the new use of the pbuf->ref
+ counter.
+
+ * DHCP dhcp_inform() crash bug fixed.
+
+ ++ Changes:
+
+ * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed
+ pbuf_refresh(). This has sped up pbuf pool operations considerably.
+ Implemented by David Haas.
+
+(0.6.1)
+
+ ++ New features:
+
+ * The packet buffer implementation has been enhanced to support
+ zero-copy and copy-on-demand for packet buffers which have their
+ payloads in application-managed memory.
+ Implemented by David Haas.
+
+ Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy
+ if an outgoing packet can be directly sent on the link, or perform
+ a copy-on-demand when necessary.
+
+ The application can safely assume the packet is sent, and the RAM
+ is available to the application directly after calling udp_send()
+ or similar function.
+
+ ++ Bugfixes:
+
+ * ARP_QUEUEING should now correctly work for all cases, including
+ PBUF_REF.
+ Implemented by Leon Woestenberg.
+
+ ++ Changes:
+
+ * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer
+ to a '0.0.0.0' IP address.
+
+ * The packet buffer implementation is changed. The pbuf->ref counter
+ meaning has changed, and several pbuf functions have been
+ adapted accordingly.
+
+ * netif drivers have to be changed to set the hardware address length field
+ that must be initialized correctly by the driver (hint: 6 for Ethernet MAC).
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+ * netif's have a dhcp field that must be initialized to NULL by the driver.
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+(0.5.x) This file has been unmaintained up to 0.6.1. All changes are
+ logged in CVS but have not been explained here.
+
+(0.5.3) Changes since version 0.5.2
+
+ ++ Bugfixes:
+
+ * memp_malloc(MEMP_API_MSG) could fail with multiple application
+ threads because it wasn't protected by semaphores.
+
+ ++ Other changes:
+
+ * struct ip_addr now packed.
+
+ * The name of the time variable in arp.c has been changed to ctime
+ to avoid conflicts with the time() function.
+
+(0.5.2) Changes since version 0.5.1
+
+ ++ New features:
+
+ * A new TCP function, tcp_tmr(), now handles both TCP timers.
+
+ ++ Bugfixes:
+
+ * A bug in tcp_parseopt() could cause the stack to hang because of a
+ malformed TCP option.
+
+ * The address of new connections in the accept() function in the BSD
+ socket library was not handled correctly.
+
+ * pbuf_dechain() did not update the ->tot_len field of the tail.
+
+ * Aborted TCP connections were not handled correctly in all
+ situations.
+
+ ++ Other changes:
+
+ * All protocol header structs are now packed.
+
+ * The ->len field in the tcp_seg structure now counts the actual
+ amount of data, and does not add one for SYN and FIN segments.
+
+(0.5.1) Changes since version 0.5.0
+
+ ++ New features:
+
+ * Possible to run as a user process under Linux.
+
+ * Preliminary support for cross platform packed structs.
+
+ * ARP timer now implemented.
+
+ ++ Bugfixes:
+
+ * TCP output queue length was badly initialized when opening
+ connections.
+
+ * TCP delayed ACKs were not sent correctly.
+
+ * Explicit initialization of BSS segment variables.
+
+ * read() in BSD socket library could drop data.
+
+ * Problems with memory alignment.
+
+ * Situations when all TCP buffers were used could lead to
+ starvation.
+
+ * TCP MSS option wasn't parsed correctly.
+
+ * Problems with UDP checksum calculation.
+
+ * IP multicast address tests had endianess problems.
+
+ * ARP requests had wrong destination hardware address.
+
+ ++ Other changes:
+
+ * struct eth_addr changed from u16_t[3] array to u8_t[6].
+
+ * A ->linkoutput() member was added to struct netif.
+
+ * TCP and UDP ->dest_* struct members where changed to ->remote_*.
+
+ * ntoh* macros are now null definitions for big endian CPUs.
+
+(0.5.0) Changes since version 0.4.2
+
+ ++ New features:
+
+ * Redesigned operating system emulation layer to make porting easier.
+
+ * Better control over TCP output buffers.
+
+ * Documenation added.
+
+ ++ Bugfixes:
+
+ * Locking issues in buffer management.
+
+ * Bugfixes in the sequential API.
+
+ * IP forwarding could cause memory leakage. This has been fixed.
+
+ ++ Other changes:
+
+ * Directory structure somewhat changed; the core/ tree has been
+ collapsed.
+
+(0.4.2) Changes since version 0.4.1
+
+ ++ New features:
+
+ * Experimental ARP implementation added.
+
+ * Skeleton Ethernet driver added.
+
+ * Experimental BSD socket API library added.
+
+ ++ Bugfixes:
+
+ * In very intense situations, memory leakage could occur. This has
+ been fixed.
+
+ ++ Other changes:
+
+ * Variables named "data" and "code" have been renamed in order to
+ avoid name conflicts in certain compilers.
+
+ * Variable++ have in appliciable cases been translated to ++variable
+ since some compilers generate better code in the latter case.
+
+(0.4.1) Changes since version 0.4
+
+ ++ New features:
+
+ * TCP: Connection attempts time out earlier than data
+ transmissions. Nagle algorithm implemented. Push flag set on the
+ last segment in a burst.
+
+ * UDP: experimental support for UDP-Lite extensions.
+
+ ++ Bugfixes:
+
+ * TCP: out of order segments were in some cases handled incorrectly,
+ and this has now been fixed. Delayed acknowledgements was broken
+ in 0.4, has now been fixed. Binding to an address that is in use
+ now results in an error. Reset connections sometimes hung an
+ application; this has been fixed.
+
+ * Checksum calculation sometimes failed for chained pbufs with odd
+ lengths. This has been fixed.
+
+ * API: a lot of bug fixes in the API. The UDP API has been improved
+ and tested. Error reporting and handling has been
+ improved. Logical flaws and race conditions for incoming TCP
+ connections has been found and removed.
+
+ * Memory manager: alignment issues. Reallocating memory sometimes
+ failed, this has been fixed.
+
+ * Generic library: bcopy was flawed and has been fixed.
+
+ ++ Other changes:
+
+ * API: all datatypes has been changed from generic ones such as
+ ints, to specified ones such as u16_t. Functions that return
+ errors now have the correct type (err_t).
+
+ * General: A lot of code cleaned up and debugging code removed. Many
+ portability issues have been fixed.
+
+ * The license was changed; the advertising clause was removed.
+
+ * C64 port added.
+
+ * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri
+ Kosunen, Mikael Caleres, and Frits Wilmink for reporting and
+ fixing bugs!
+
+(0.4) Changes since version 0.3.1
+
+ * Memory management has been radically changed; instead of
+ allocating memory from a shared heap, memory for objects that are
+ rapidly allocated and deallocated is now kept in pools. Allocation
+ and deallocation from those memory pools is very fast. The shared
+ heap is still present but is used less frequently.
+
+ * The memory, memory pool, and packet buffer subsystems now support
+ 4-, 2-, or 1-byte alignment.
+
+ * "Out of memory" situations are handled in a more robust way.
+
+ * Stack usage has been reduced.
+
+ * Easier configuration of lwIP parameters such as memory usage,
+ TTLs, statistics gathering, etc. All configuration parameters are
+ now kept in a single header file "lwipopts.h".
+
+ * The directory structure has been changed slightly so that all
+ architecture specific files are kept under the src/arch
+ hierarchy.
+
+ * Error propagation has been improved, both in the protocol modules
+ and in the API.
+
+ * The code for the RTXC architecture has been implemented, tested
+ and put to use.
+
+ * Bugs have been found and corrected in the TCP, UDP, IP, API, and
+ the Internet checksum modules.
+
+ * Bugs related to porting between a 32-bit and a 16-bit architecture
+ have been found and corrected.
+
+ * The license has been changed slightly to conform more with the
+ original BSD license, including the advertisement clause.
+
+(0.3.1) Changes since version 0.3
+
+ * Fix of a fatal bug in the buffer management. Pbufs with allocated
+ RAM never returned the RAM when the pbuf was deallocated.
+
+ * TCP congestion control, window updates and retransmissions did not
+ work correctly. This has now been fixed.
+
+ * Bugfixes in the API.
+
+(0.3) Changes since version 0.2
+
+ * New and improved directory structure. All include files are now
+ kept in a dedicated include/ directory.
+
+ * The API now has proper error handling. A new function,
+ netconn_err(), now returns an error code for the connection in
+ case of errors.
+
+ * Improvements in the memory management subsystem. The system now
+ keeps a pointer to the lowest free memory block. A new function,
+ mem_malloc2() tries to allocate memory once, and if it fails tries
+ to free some memory and retry the allocation.
+
+ * Much testing has been done with limited memory
+ configurations. lwIP now does a better job when overloaded.
+
+ * Some bugfixes and improvements to the buffer (pbuf) subsystem.
+
+ * Many bugfixes in the TCP code:
+
+ - Fixed a bug in tcp_close().
+
+ - The TCP receive window was incorrectly closed when out of
+ sequence segments was received. This has been fixed.
+
+ - Connections are now timed-out of the FIN-WAIT-2 state.
+
+ - The initial congestion window could in some cases be too
+ large. This has been fixed.
+
+ - The retransmission queue could in some cases be screwed up. This
+ has been fixed.
+
+ - TCP RST flag now handled correctly.
+
+ - Out of sequence data was in some cases never delivered to the
+ application. This has been fixed.
+
+ - Retransmitted segments now contain the correct acknowledgment
+ number and advertised window.
+
+ - TCP retransmission timeout backoffs are not correctly computed
+ (ala BSD). After a number of retransmissions, TCP now gives up
+ the connection.
+
+ * TCP connections now are kept on three lists, one for active
+ connections, one for listening connections, and one for
+ connections that are in TIME-WAIT. This greatly speeds up the fast
+ timeout processing for sending delayed ACKs.
+
+ * TCP now provides proper feedback to the application when a
+ connection has been successfully set up.
+
+ * More comments have been added to the code. The code has also been
+ somewhat cleaned up.
+
+(0.2) Initial public release.
diff --git a/core/lwip/COPYING b/core/lwip/COPYING
new file mode 100644
index 00000000..e23898b5
--- /dev/null
+++ b/core/lwip/COPYING
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
diff --git a/core/lwip/FILES b/core/lwip/FILES
new file mode 100644
index 00000000..66253196
--- /dev/null
+++ b/core/lwip/FILES
@@ -0,0 +1,4 @@
+src/ - The source code for the lwIP TCP/IP stack.
+doc/ - The documentation for lwIP.
+
+See also the FILES file in each subdirectory.
diff --git a/core/lwip/README b/core/lwip/README
new file mode 100644
index 00000000..a62cc4f3
--- /dev/null
+++ b/core/lwip/README
@@ -0,0 +1,89 @@
+INTRODUCTION
+
+lwIP is a small independent implementation of the TCP/IP protocol
+suite that has been developed by Adam Dunkels at the Computer and
+Networks Architectures (CNA) lab at the Swedish Institute of Computer
+Science (SICS).
+
+The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
+while still having a full scale TCP. This making lwIP suitable for use
+in embedded systems with tens of kilobytes of free RAM and room for
+around 40 kilobytes of code ROM.
+
+FEATURES
+
+ * IP (Internet Protocol) including packet forwarding over multiple network
+ interfaces
+ * ICMP (Internet Control Message Protocol) for network maintenance and debugging
+ * IGMP (Internet Group Management Protocol) for multicast traffic management
+ * UDP (User Datagram Protocol) including experimental UDP-lite extensions
+ * TCP (Transmission Control Protocol) with congestion control, RTT estimation
+ and fast recovery/fast retransmit
+ * Specialized raw/native API for enhanced performance
+ * Optional Berkeley-like socket API
+ * DNS (Domain names resolver)
+ * SNMP (Simple Network Management Protocol)
+ * DHCP (Dynamic Host Configuration Protocol)
+ * AUTOIP (for IPv4, conform with RFC 3927)
+ * PPP (Point-to-Point Protocol)
+ * ARP (Address Resolution Protocol) for Ethernet
+
+LICENSE
+
+lwIP is freely available under a BSD license.
+
+DEVELOPMENT
+
+lwIP has grown into an excellent TCP/IP stack for embedded devices,
+and developers using the stack often submit bug fixes, improvements,
+and additions to the stack to further increase its usefulness.
+
+Development of lwIP is hosted on Savannah, a central point for
+software development, maintenance and distribution. Everyone can
+help improve lwIP by use of Savannah's interface, CVS and the
+mailing list. A core team of developers will commit changes to the
+CVS source tree.
+
+The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
+contributions (such as platform ports) are in the 'contrib' module.
+
+See doc/savannah.txt for details on CVS server access for users and
+developers.
+
+Last night's CVS tar ball can be downloaded from:
+ http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING]
+
+The current CVS trees are web-browsable:
+ http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
+ http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
+
+Submit patches and bugs via the lwIP project page:
+ http://savannah.nongnu.org/projects/lwip/
+
+
+DOCUMENTATION
+
+The original out-dated homepage of lwIP and Adam Dunkels' papers on
+lwIP are at the official lwIP home page:
+ http://www.sics.se/~adam/lwip/
+
+Self documentation of the source code is regularly extracted from the
+current CVS sources and is available from this web page:
+ http://www.nongnu.org/lwip/
+
+There is now a constantly growin wiki about lwIP at
+ http://lwip.wikia.com/wiki/LwIP_Wiki
+
+Also, there are mailing lists you can subscribe at
+ http://savannah.nongnu.org/mail/?group=lwip
+plus searchable archives:
+ http://lists.nongnu.org/archive/html/lwip-users/
+ http://lists.nongnu.org/archive/html/lwip-devel/
+
+Reading Adam's papers, the files in docs/, browsing the source code
+documentation and browsing the mailing list archives is a good way to
+become familiar with the design of lwIP.
+
+Adam Dunkels <adam@sics.se>
+Leon Woestenberg <leon.woestenberg@gmx.net>
+
diff --git a/core/lwip/UPGRADING b/core/lwip/UPGRADING
new file mode 100644
index 00000000..6501107a
--- /dev/null
+++ b/core/lwip/UPGRADING
@@ -0,0 +1,144 @@
+This file lists major changes between release versions that require
+ports or applications to be changed. Use it to update a port or an
+application written for an older version of lwIP to correctly work
+with newer versions.
+
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ Application changes:
+
+ * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
+ compatibility to old applications, but will be removed in the future).
+
+ * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
+
+ +++ Raw API:
+ * Changed the semantics of tcp_close() (since it was rather a
+ shutdown before): Now the application does *NOT* get any calls to the recv
+ callback (aside from NULL/closed) after calling tcp_close()
+
+ * When calling tcp_abort() from a raw API TCP callback function,
+ make sure you return ERR_ABRT to prevent accessing unallocated memory.
+ (ERR_ABRT now means the applicaiton has called tcp_abort!)
+
+ +++ Netconn API:
+ * Changed netconn_receive() and netconn_accept() to return
+ err_t, not a pointer to new data/netconn.
+
+ +++ Socket API:
+ * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
+ now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
+
+ * Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ +++ all APIs:
+ * correctly implemented SO(F)_REUSEADDR
+
+ ++ Port changes
+
+ +++ new files:
+
+ * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
+
+ * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
+ the actual application programmer's API
+
+ * Separated timer implementation from sys.h/.c, moved to timers.h/.c;
+ Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
+ still want to use your own timer implementation for NO_SYS==0 (as before).
+
+ +++ sys layer:
+
+ * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
+ sys_sem_t;
+
+ * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+
+ * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
+ binary semaphores instead of mutexes - as before)
+
+ +++ new options:
+
+ * Don't waste memory when chaining segments, added option TCP_OVERSIZE to
+ prevent creating many small pbufs when calling tcp_write with many small
+ blocks of data. Instead, pbufs are allocated larger than needed and the
+ space is used for later calls to tcp_write.
+
+ * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
+ in tcp_write/udp_send.
+
+ * Added an additional option LWIP_ETHERNET to support ethernet without ARP
+ (necessary for pure PPPoE)
+
+ * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
+ be used to place these pools into user-defined memory by using external
+ declaration.
+
+ * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
+
+ +++ new pools:
+
+ * Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
+ so MEMP_NUM_NETDB has to be set accordingly.
+
+ * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
+ MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
+
+ * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
+ to be set accordingly.
+
+ * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
+ has to be set accordingly
+
+ * Integrated loopif into netif.c - loopif does not have to be created by the
+ port any more, just define LWIP_HAVE_LOOPIF to 1.
+
+ * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
+ in cc.h, e.g. used by igmp)
+
+ * Added printf-formatter X8_F to printf u8_t as hex
+
+ * The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+
+ * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
+ with user-allocated structs instead of calling mem_malloc
+
+ * Added const char* name to mem- and memp-stats for easier debugging.
+
+ * Calculate the TCP/UDP checksum while copying to only fetch data once:
+ Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
+
+ * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
+ more than one pcb.
+
+ * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
+ off any more, if this is set to 0, only one packet (the most recent one) is
+ queued (like demanded by RFC 1122).
+
+
+ ++ Major bugfixes/improvements
+
+ * Implemented tcp_shutdown() to only shut down one end of a connection
+ * Implemented shutdown() at socket- and netconn-level
+ * Added errorset support to select() + improved select speed overhead
+ * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
+ * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
+ * Use macros defined in ip_addr.h to work with IP addresses
+ * Implemented many nonblocking socket/netconn functions
+ * Fixed ARP input processing: only add a new entry if a request was directed as us
+ * mem_realloc() to mem_trim() to prevent confusion with realloc()
+ * Some improvements for AutoIP (don't route/forward link-local addresses, don't break
+ existing connections when assigning a routable address)
+ * Correctly handle remote side overrunning our rcv_wnd in ooseq case
+ * Removed packing from ip_addr_t, the packed version is now only used in protocol headers
+ * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
+ * Added support for static ARP table entries
+
+(STABLE-1.3.2)
+
+ * initial version of this file
diff --git a/core/lwip/doc/FILES b/core/lwip/doc/FILES
new file mode 100644
index 00000000..05d356f4
--- /dev/null
+++ b/core/lwip/doc/FILES
@@ -0,0 +1,6 @@
+savannah.txt - How to obtain the current development source code.
+contrib.txt - How to contribute to lwIP as a developer.
+rawapi.txt - The documentation for the core API of lwIP.
+ Also provides an overview about the other APIs and multithreading.
+snmp_agent.txt - The documentation for the lwIP SNMP agent.
+sys_arch.txt - The documentation for a system abstraction layer of lwIP.
diff --git a/core/lwip/doc/contrib.txt b/core/lwip/doc/contrib.txt
new file mode 100644
index 00000000..39596fca
--- /dev/null
+++ b/core/lwip/doc/contrib.txt
@@ -0,0 +1,63 @@
+1 Introduction
+
+This document describes some guidelines for people participating
+in lwIP development.
+
+2 How to contribute to lwIP
+
+Here is a short list of suggestions to anybody working with lwIP and
+trying to contribute bug reports, fixes, enhancements, platform ports etc.
+First of all as you may already know lwIP is a volunteer project so feedback
+to fixes or questions might often come late. Hopefully the bug and patch tracking
+features of Savannah help us not lose users' input.
+
+2.1 Source code style:
+
+1. do not use tabs.
+2. indentation is two spaces per level (i.e. per tab).
+3. end debug messages with a trailing newline (\n).
+4. one space between keyword and opening bracket.
+5. no space between function and opening bracket.
+6. one space and no newline before opening curly braces of a block.
+7. closing curly brace on a single line.
+8. spaces surrounding assignment and comparisons.
+9. don't initialize static and/or global variables to zero, the compiler takes care of that.
+10. use current source code style as further reference.
+
+2.2 Source code documentation style:
+
+1. JavaDoc compliant and Doxygen compatible.
+2. Function documentation above functions in .c files, not .h files.
+ (This forces you to synchronize documentation and implementation.)
+3. Use current documentation style as further reference.
+
+2.3 Bug reports and patches:
+
+1. Make sure you are reporting bugs or send patches against the latest
+ sources. (From the latest release and/or the current CVS sources.)
+2. If you think you found a bug make sure it's not already filed in the
+ bugtracker at Savannah.
+3. If you have a fix put the patch on Savannah. If it is a patch that affects
+ both core and arch specific stuff please separate them so that the core can
+ be applied separately while leaving the other patch 'open'. The prefered way
+ is to NOT touch archs you can't test and let maintainers take care of them.
+ This is a good way to see if they are used at all - the same goes for unix
+ netifs except tapif.
+4. Do not file a bug and post a fix to it to the patch area. Either a bug report
+ or a patch will be enough.
+ If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
+5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two)
+ can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded
+ as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead
+ for reporting a compiler warning fix.
+6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other
+ trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you
+ change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than
+ if it's not to the point and long :) so the chances for it to be applied are greater.
+
+2.4 Platform porters:
+
+1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
+ you think it could benefit others[1] you might want discuss this on the mailing list. You
+ can also ask for CVS access to submit and maintain your port in the contrib CVS module.
+ \ No newline at end of file
diff --git a/core/lwip/doc/rawapi.txt b/core/lwip/doc/rawapi.txt
new file mode 100644
index 00000000..c727da99
--- /dev/null
+++ b/core/lwip/doc/rawapi.txt
@@ -0,0 +1,505 @@
+Raw TCP/IP interface for lwIP
+
+Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons
+
+lwIP provides three Application Program's Interfaces (APIs) for programs
+to use for communication with the TCP/IP code:
+* low-level "core" / "callback" or "raw" API.
+* higher-level "sequential" API.
+* BSD-style socket API.
+
+The sequential API provides a way for ordinary, sequential, programs
+to use the lwIP stack. It is quite similar to the BSD socket API. The
+model of execution is based on the blocking open-read-write-close
+paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP
+code and the application program must reside in different execution
+contexts (threads).
+
+The socket API is a compatibility API for existing applications,
+currently it is built on top of the sequential API. It is meant to
+provide all functions needed to run socket API applications running
+on other platforms (e.g. unix / windows etc.). However, due to limitations
+in the specification of this API, there might be incompatibilities
+that require small modifications of existing programs.
+
+** Threading
+
+lwIP started targeting single-threaded environments. When adding multi-
+threading support, instead of making the core thread-safe, another
+approach was chosen: there is one main thread running the lwIP core
+(also known as the "tcpip_thread"). The raw API may only be used from
+this thread! Application threads using the sequential- or socket API
+communicate with this main thread through message passing.
+
+ As such, the list of functions that may be called from
+ other threads or an ISR is very limited! Only functions
+ from these API header files are thread-safe:
+ - api.h
+ - netbuf.h
+ - netdb.h
+ - netifapi.h
+ - sockets.h
+ - sys.h
+
+ Additionaly, memory (de-)allocation functions may be
+ called from multiple threads (not ISR!) with NO_SYS=0
+ since they are protected by SYS_LIGHTWEIGHT_PROT and/or
+ semaphores.
+
+ Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1
+ and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
+ pbuf_free() may also be called from another thread or
+ an ISR (since only then, mem_free - for PBUF_RAM - may
+ be called from an ISR: otherwise, the HEAP is only
+ protected by semaphores).
+
+
+** The remainder of this document discusses the "raw" API. **
+
+The raw TCP/IP interface allows the application program to integrate
+better with the TCP/IP code. Program execution is event based by
+having callback functions being called from within the TCP/IP
+code. The TCP/IP code and the application program both run in the same
+thread. The sequential API has a much higher overhead and is not very
+well suited for small systems since it forces a multithreaded paradigm
+on the application.
+
+The raw TCP/IP interface is not only faster in terms of code execution
+time but is also less memory intensive. The drawback is that program
+development is somewhat harder and application programs written for
+the raw TCP/IP interface are more difficult to understand. Still, this
+is the preferred way of writing applications that should be small in
+code size and memory usage.
+
+Both APIs can be used simultaneously by different application
+programs. In fact, the sequential API is implemented as an application
+program using the raw TCP/IP interface.
+
+--- Callbacks
+
+Program execution is driven by callbacks. Each callback is an ordinary
+C function that is called from within the TCP/IP code. Every callback
+function is passed the current TCP or UDP connection state as an
+argument. Also, in order to be able to keep program specific state,
+the callback functions are called with a program specified argument
+that is independent of the TCP/IP state.
+
+The function for setting the application connection state is:
+
+- void tcp_arg(struct tcp_pcb *pcb, void *arg)
+
+ Specifies the program specific state that should be passed to all
+ other callback functions. The "pcb" argument is the current TCP
+ connection control block, and the "arg" argument is the argument
+ that will be passed to the callbacks.
+
+
+--- TCP connection setup
+
+The functions used for setting up connections is similar to that of
+the sequential API and of the BSD socket API. A new TCP connection
+identifier (i.e., a protocol control block - PCB) is created with the
+tcp_new() function. This PCB can then be either set to listen for new
+incoming connections or be explicitly connected to another host.
+
+- struct tcp_pcb *tcp_new(void)
+
+ Creates a new connection identifier (PCB). If memory is not
+ available for creating the new pcb, NULL is returned.
+
+- err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port)
+
+ Binds the pcb to a local IP address and port number. The IP address
+ can be specified as IP_ADDR_ANY in order to bind the connection to
+ all local IP addresses.
+
+ If another connection is bound to the same port, the function will
+ return ERR_USE, otherwise ERR_OK is returned.
+
+- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
+
+ Commands a pcb to start listening for incoming connections. When an
+ incoming connection is accepted, the function specified with the
+ tcp_accept() function will be called. The pcb will have to be bound
+ to a local port with the tcp_bind() function.
+
+ The tcp_listen() function returns a new connection identifier, and
+ the one passed as an argument to the function will be
+ deallocated. The reason for this behavior is that less memory is
+ needed for a connection that is listening, so tcp_listen() will
+ reclaim the memory needed for the original connection and allocate a
+ new smaller memory block for the listening connection.
+
+ tcp_listen() may return NULL if no memory was available for the
+ listening connection. If so, the memory associated with the pcb
+ passed as an argument to tcp_listen() will not be deallocated.
+
+- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+
+ Same as tcp_listen, but limits the number of outstanding connections
+ in the listen queue to the value specified by the backlog argument.
+ To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
+
+- void tcp_accepted(struct tcp_pcb *pcb)
+
+ Inform lwIP that an incoming connection has been accepted. This would
+ usually be called from the accept callback. This allows lwIP to perform
+ housekeeping tasks, such as allowing further incoming connections to be
+ queued in the listen backlog.
+
+- void tcp_accept(struct tcp_pcb *pcb,
+ err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
+ err_t err))
+
+ Specified the callback function that should be called when a new
+ connection arrives on a listening connection.
+
+- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port, err_t (* connected)(void *arg,
+ struct tcp_pcb *tpcb,
+ err_t err));
+
+ Sets up the pcb to connect to the remote host and sends the
+ initial SYN segment which opens the connection.
+
+ The tcp_connect() function returns immediately; it does not wait for
+ the connection to be properly setup. Instead, it will call the
+ function specified as the fourth argument (the "connected" argument)
+ when the connection is established. If the connection could not be
+ properly established, either because the other host refused the
+ connection or because the other host didn't answer, the "err"
+ callback function of this pcb (registered with tcp_err, see below)
+ will be called.
+
+ The tcp_connect() function can return ERR_MEM if no memory is
+ available for enqueueing the SYN segment. If the SYN indeed was
+ enqueued successfully, the tcp_connect() function returns ERR_OK.
+
+
+--- Sending TCP data
+
+TCP data is sent by enqueueing the data with a call to
+tcp_write(). When the data is successfully transmitted to the remote
+host, the application will be notified with a call to a specified
+callback function.
+
+- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len,
+ u8_t copy)
+
+ Enqueues the data pointed to by the argument dataptr. The length of
+ the data is passed as the len parameter. The copy argument is either
+ 0 or 1 and indicates whether the new memory should be allocated for
+ the data to be copied into. If the argument is 0, no new memory
+ should be allocated and the data should only be referenced by
+ pointer.
+
+ The tcp_write() function will fail and return ERR_MEM if the length
+ of the data exceeds the current send buffer size or if the length of
+ the queue of outgoing segment is larger than the upper limit defined
+ in lwipopts.h. The number of bytes available in the output queue can
+ be retrieved with the tcp_sndbuf() function.
+
+ The proper way to use this function is to call the function with at
+ most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
+ the application should wait until some of the currently enqueued
+ data has been successfully received by the other host and try again.
+
+- void tcp_sent(struct tcp_pcb *pcb,
+ err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
+ u16_t len))
+
+ Specifies the callback function that should be called when data has
+ successfully been received (i.e., acknowledged) by the remote
+ host. The len argument passed to the callback function gives the
+ amount bytes that was acknowledged by the last acknowledgment.
+
+
+--- Receiving TCP data
+
+TCP data reception is callback based - an application specified
+callback function is called when new data arrives. When the
+application has taken the data, it has to call the tcp_recved()
+function to indicate that TCP can advertise increase the receive
+window.
+
+- void tcp_recv(struct tcp_pcb *pcb,
+ err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
+ struct pbuf *p, err_t err))
+
+ Sets the callback function that will be called when new data
+ arrives. The callback function will be passed a NULL pbuf to
+ indicate that the remote host has closed the connection. If
+ there are no errors and the callback function is to return
+ ERR_OK, then it must free the pbuf. Otherwise, it must not
+ free the pbuf so that lwIP core code can store it.
+
+- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
+
+ Must be called when the application has received the data. The len
+ argument indicates the length of the received data.
+
+
+--- Application polling
+
+When a connection is idle (i.e., no data is either transmitted or
+received), lwIP will repeatedly poll the application by calling a
+specified callback function. This can be used either as a watchdog
+timer for killing connections that have stayed idle for too long, or
+as a method of waiting for memory to become available. For instance,
+if a call to tcp_write() has failed because memory wasn't available,
+the application may use the polling functionality to call tcp_write()
+again when the connection has been idle for a while.
+
+- void tcp_poll(struct tcp_pcb *pcb,
+ err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
+ u8_t interval)
+
+ Specifies the polling interval and the callback function that should
+ be called to poll the application. The interval is specified in
+ number of TCP coarse grained timer shots, which typically occurs
+ twice a second. An interval of 10 means that the application would
+ be polled every 5 seconds.
+
+
+--- Closing and aborting connections
+
+- err_t tcp_close(struct tcp_pcb *pcb)
+
+ Closes the connection. The function may return ERR_MEM if no memory
+ was available for closing the connection. If so, the application
+ should wait and try again either by using the acknowledgment
+ callback or the polling functionality. If the close succeeds, the
+ function returns ERR_OK.
+
+ The pcb is deallocated by the TCP code after a call to tcp_close().
+
+- void tcp_abort(struct tcp_pcb *pcb)
+
+ Aborts the connection by sending a RST (reset) segment to the remote
+ host. The pcb is deallocated. This function never fails.
+
+ ATTENTION: When calling this from one of the TCP callbacks, make
+ sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ or you will risk accessing deallocated memory or memory leaks!
+
+
+If a connection is aborted because of an error, the application is
+alerted of this event by the err callback. Errors that might abort a
+connection are when there is a shortage of memory. The callback
+function to be called is set using the tcp_err() function.
+
+- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
+ err_t err))
+
+ The error callback function does not get the pcb passed to it as a
+ parameter since the pcb may already have been deallocated.
+
+
+--- Lower layer TCP interface
+
+TCP provides a simple interface to the lower layers of the
+system. During system initialization, the function tcp_init() has
+to be called before any other TCP function is called. When the system
+is running, the two timer functions tcp_fasttmr() and tcp_slowtmr()
+must be called with regular intervals. The tcp_fasttmr() should be
+called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and
+tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds.
+
+
+--- UDP interface
+
+The UDP interface is similar to that of TCP, but due to the lower
+level of complexity of UDP, the interface is significantly simpler.
+
+- struct udp_pcb *udp_new(void)
+
+ Creates a new UDP pcb which can be used for UDP communication. The
+ pcb is not active until it has either been bound to a local address
+ or connected to a remote address.
+
+- void udp_remove(struct udp_pcb *pcb)
+
+ Removes and deallocates the pcb.
+
+- err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port)
+
+ Binds the pcb to a local address. The IP-address argument "ipaddr"
+ can be IP_ADDR_ANY to indicate that it should listen to any local IP
+ address. The function currently always return ERR_OK.
+
+- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port)
+
+ Sets the remote end of the pcb. This function does not generate any
+ network traffic, but only set the remote address of the pcb.
+
+- err_t udp_disconnect(struct udp_pcb *pcb)
+
+ Remove the remote end of the pcb. This function does not generate
+ any network traffic, but only removes the remote address of the pcb.
+
+- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
+
+ Sends the pbuf p. The pbuf is not deallocated.
+
+- void udp_recv(struct udp_pcb *pcb,
+ void (* recv)(void *arg, struct udp_pcb *upcb,
+ struct pbuf *p,
+ struct ip_addr *addr,
+ u16_t port),
+ void *recv_arg)
+
+ Specifies a callback function that should be called when a UDP
+ datagram is received.
+
+
+--- System initalization
+
+A truly complete and generic sequence for initializing the lwip stack
+cannot be given because it depends on the build configuration (lwipopts.h)
+and additional initializations for your runtime environment (e.g. timers).
+
+We can give you some idea on how to proceed when using the raw API.
+We assume a configuration using a single Ethernet netif and the
+UDP and TCP transport layers, IPv4 and the DHCP client.
+
+Call these functions in the order of appearance:
+
+- stats_init()
+
+ Clears the structure where runtime statistics are gathered.
+
+- sys_init()
+
+ Not of much use since we set the NO_SYS 1 option in lwipopts.h,
+ to be called for easy configuration changes.
+
+- lwip_mem_init()
+
+ Initializes the dynamic memory heap defined by MEM_SIZE.
+
+- memp_init()
+
+ Initializes the memory pools defined by MEMP_NUM_x.
+
+- pbuf_init()
+
+ Initializes the pbuf memory pool defined by PBUF_POOL_SIZE.
+
+- etharp_init()
+
+ Initializes the ARP table and queue.
+ Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval
+ after this initialization.
+
+- ip_init()
+
+ Doesn't do much, it should be called to handle future changes.
+
+- udp_init()
+
+ Clears the UDP PCB list.
+
+- tcp_init()
+
+ Clears the TCP PCB list and clears some internal TCP timers.
+ Note: you must call tcp_fasttmr() and tcp_slowtmr() at the
+ predefined regular intervals after this initialization.
+
+- netif_add(struct netif *netif, struct ip_addr *ipaddr,
+ struct ip_addr *netmask, struct ip_addr *gw,
+ void *state, err_t (* init)(struct netif *netif),
+ err_t (* input)(struct pbuf *p, struct netif *netif))
+
+ Adds your network interface to the netif_list. Allocate a struct
+ netif and pass a pointer to this structure as the first argument.
+ Give pointers to cleared ip_addr structures when using DHCP,
+ or fill them with sane numbers otherwise. The state pointer may be NULL.
+
+ The init function pointer must point to a initialization function for
+ your ethernet netif interface. The following code illustrates it's use.
+
+ err_t netif_if_init(struct netif *netif)
+ {
+ u8_t i;
+
+ for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i];
+ init_my_eth_device();
+ return ERR_OK;
+ }
+
+ For ethernet drivers, the input function pointer must point to the lwip
+ function ethernet_input() declared in "netif/etharp.h". Other drivers
+ must use ip_input() declared in "lwip/ip.h".
+
+- netif_set_default(struct netif *netif)
+
+ Registers the default network interface.
+
+- netif_set_up(struct netif *netif)
+
+ When the netif is fully configured this function must be called.
+
+- dhcp_start(struct netif *netif)
+
+ Creates a new DHCP client for this interface on the first call.
+ Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
+ the predefined regular intervals after starting the client.
+
+ You can peek in the netif->dhcp struct for the actual DHCP status.
+
+
+--- Optimalization hints
+
+The first thing you want to optimize is the lwip_standard_checksum()
+routine from src/core/inet.c. You can override this standard
+function with the #define LWIP_CHKSUM <your_checksum_routine>.
+
+There are C examples given in inet.c or you might want to
+craft an assembly function for this. RFC1071 is a good
+introduction to this subject.
+
+Other significant improvements can be made by supplying
+assembly or inline replacements for htons() and htonl()
+if you're using a little-endian architecture.
+#define LWIP_PLATFORM_BYTESWAP 1
+#define LWIP_PLATFORM_HTONS(x) <your_htons>
+#define LWIP_PLATFORM_HTONL(x) <your_htonl>
+
+Check your network interface driver if it reads at
+a higher speed than the maximum wire-speed. If the
+hardware isn't serviced frequently and fast enough
+buffer overflows are likely to occur.
+
+E.g. when using the cs8900 driver, call cs8900if_service(ethif)
+as frequently as possible. When using an RTOS let the cs8900 interrupt
+wake a high priority task that services your driver using a binary
+semaphore or event flag. Some drivers might allow additional tuning
+to match your application and network.
+
+For a production release it is recommended to set LWIP_STATS to 0.
+Note that speed performance isn't influenced much by simply setting
+high values to the memory options.
+
+For more optimization hints take a look at the lwIP wiki.
+
+--- Zero-copy MACs
+
+To achieve zero-copy on transmit, the data passed to the raw API must
+remain unchanged until sent. Because the send- (or write-)functions return
+when the packets have been enqueued for sending, data must be kept stable
+after that, too.
+
+This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
+must *not* be reused by the application unless their ref-count is 1.
+
+For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
+but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
+PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
+
+Also, data passed to tcp_write without the copy-flag must not be changed!
+
+Therefore, be careful which type of PBUF you use and if you copy TCP data
+or not!
diff --git a/core/lwip/doc/savannah.txt b/core/lwip/doc/savannah.txt
new file mode 100644
index 00000000..409905b1
--- /dev/null
+++ b/core/lwip/doc/savannah.txt
@@ -0,0 +1,135 @@
+Daily Use Guide for using Savannah for lwIP
+
+Table of Contents:
+
+1 - Obtaining lwIP from the CVS repository
+2 - Committers/developers CVS access using SSH (to be written)
+3 - Merging from DEVEL branch to main trunk (stable branch)
+4 - How to release lwIP
+
+
+
+1 Obtaining lwIP from the CVS repository
+----------------------------------------
+
+To perform an anonymous CVS checkout of the main trunk (this is where
+bug fixes and incremental enhancements occur), do this:
+
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip
+
+Or, obtain a stable branch (updated with bug fixes only) as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+ -r STABLE-0_7 -d lwip-0.7 lwip
+
+Or, obtain a specific (fixed) release as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+ -r STABLE-0_7_0 -d lwip-0.7.0 lwip
+
+3 Committers/developers CVS access using SSH
+--------------------------------------------
+
+The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
+As such, CVS commits to the server occur through a SSH tunnel for project members.
+To create a SSH2 key pair in UNIX-like environments, do this:
+
+ssh-keygen -t dsa
+
+Under Windows, a recommended SSH client is "PuTTY", freely available with good
+documentation and a graphic user interface. Use its key generator.
+
+Now paste the id_dsa.pub contents into your Savannah account public key list. Wait
+a while so that Savannah can update its configuration (This can take minutes).
+
+Try to login using SSH:
+
+ssh -v your_login@cvs.sv.gnu.org
+
+If it tells you:
+
+Authenticating with public key "your_key_name"...
+Server refused to allocate pty
+
+then you could login; Savannah refuses to give you a shell - which is OK, as we
+are allowed to use SSH for CVS only. Now, you should be able to do this:
+
+export CVS_RSH=ssh
+cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip
+
+after which you can edit your local files with bug fixes or new features and
+commit them. Make sure you know what you are doing when using CVS to make
+changes on the repository. If in doubt, ask on the lwip-members mailing list.
+
+(If SSH asks about authenticity of the host, you can check the key
+ fingerprint against http://savannah.nongnu.org/cvs/?group=lwip)
+
+
+3 Merging from DEVEL branch to main trunk (stable)
+--------------------------------------------------
+
+Merging is a delicate process in CVS and requires the
+following disciplined steps in order to prevent conflicts
+in the future. Conflicts can be hard to solve!
+
+Merging from branch A to branch B requires that the A branch
+has a tag indicating the previous merger. This tag is called
+'merged_from_A_to_B'. After merging, the tag is moved in the
+A branch to remember this merger for future merge actions.
+
+IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE
+REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE
+MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME).
+
+Merge all changes in DEVEL since our last merge to main:
+
+In the working copy of the main trunk:
+cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL
+
+(This will apply the changes between 'merged_from_DEVEL_to_main'
+and 'DEVEL' to your work set of files)
+
+We can now commit the merge result.
+cvs commit -R -m "Merged from DEVEL to main."
+
+If this worked out OK, we now move the tag in the DEVEL branch
+to this merge point, so we can use this point for future merges:
+
+cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip
+
+4 How to release lwIP
+---------------------
+
+First, checkout a clean copy of the branch to be released. Tag this set with
+tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example).
+
+Login CVS using pserver authentication, then export a clean copy of the
+tagged tree. Export is similar to a checkout, except that the CVS metadata
+is not created locally.
+
+export CVS_RSH=ssh
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+ -r STABLE-0_6_3 -d lwip-0.6.3 lwip
+
+Archive this directory using tar, gzip'd, bzip2'd and zip'd.
+
+tar czvf lwip-0.6.3.tar.gz lwip-0.6.3
+tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3
+zip -r lwip-0.6.3.zip lwip-0.6.3
+
+Now, sign the archives with a detached GPG binary signature as follows:
+
+gpg -b lwip-0.6.3.tar.gz
+gpg -b lwip-0.6.3.tar.bz2
+gpg -b lwip-0.6.3.zip
+
+Upload these files using anonymous FTP:
+ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
+
+ncftp>mput *0.6.3.*
+
+Additionally, you may post a news item on Savannah, like this:
+
+A new 0.6.3 release is now available here:
+http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
+
+You will have to submit this via the user News interface, then approve
+this via the Administrator News interface. \ No newline at end of file
diff --git a/core/lwip/doc/snmp_agent.txt b/core/lwip/doc/snmp_agent.txt
new file mode 100644
index 00000000..2653230f
--- /dev/null
+++ b/core/lwip/doc/snmp_agent.txt
@@ -0,0 +1,181 @@
+SNMPv1 agent for lwIP
+
+Author: Christiaan Simons
+
+This is a brief introduction how to use and configure the SNMP agent.
+Note the agent uses the raw-API UDP interface so you may also want to
+read rawapi.txt to gain a better understanding of the SNMP message handling.
+
+0 Agent Capabilities
+====================
+
+SNMPv1 per RFC1157
+ This is an old(er) standard but is still widely supported.
+ For SNMPv2c and v3 have a greater complexity and need many
+ more lines of code. IMHO this breaks the idea of "lightweight IP".
+
+ Note the S in SNMP stands for "Simple". Note that "Simple" is
+ relative. SNMP is simple compared to the complex ISO network
+ management protocols CMIP (Common Management Information Protocol)
+ and CMOT (CMip Over Tcp).
+
+MIB II per RFC1213
+ The standard lwIP stack management information base.
+ This is a required MIB, so this is always enabled.
+ When builing lwIP without TCP, the mib-2.tcp group is omitted.
+ The groups EGP, CMOT and transmission are disabled by default.
+
+ Most mib-2 objects are not writable except:
+ sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ Writing to or changing the ARP and IP address and route
+ tables is not possible.
+
+ Note lwIP has a very limited notion of IP routing. It currently
+ doen't have a route table and doesn't have a notion of the U,G,H flags.
+ Instead lwIP uses the interface list with only one default interface
+ acting as a single gateway interface (G) for the default route.
+
+ The agent returns a "virtual table" with the default route 0.0.0.0
+ for the default interface and network routes (no H) for each
+ network interface in the netif_list.
+ All routes are considered to be up (U).
+
+Loading additional MIBs
+ MIBs can only be added in compile-time, not in run-time.
+ There is no MIB compiler thus additional MIBs must be hand coded.
+
+Large SNMP message support
+ The packet decoding and encoding routines are designed
+ to use pbuf-chains. Larger payloads than the minimum
+ SNMP requirement of 484 octets are supported if the
+ PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your
+ local requirement.
+
+1 Building the Agent
+====================
+
+First of all you'll need to add the following define
+to your local lwipopts.h:
+
+#define LWIP_SNMP 1
+
+and add the source files in lwip/src/core/snmp
+and some snmp headers in lwip/src/include/lwip to your makefile.
+
+Note you'll might need to adapt you network driver to update
+the mib2 variables for your interface.
+
+2 Running the Agent
+===================
+
+The following function calls must be made in your program to
+actually get the SNMP agent running.
+
+Before starting the agent you should supply pointers
+to non-volatile memory for sysContact, sysLocation,
+and snmpEnableAuthenTraps. You can do this by calling
+
+snmp_set_syscontact()
+snmp_set_syslocation()
+snmp_set_snmpenableauthentraps()
+
+Additionally you may want to set
+
+snmp_set_sysdescr()
+snmp_set_sysobjid() (if you have a private MIB)
+snmp_set_sysname()
+
+Also before starting the agent you need to setup
+one or more trap destinations using these calls:
+
+snmp_trap_dst_enable();
+snmp_trap_dst_ip_set();
+
+In the lwIP initialisation sequence call snmp_init() just after
+the call to udp_init().
+
+Exactly every 10 msec the SNMP uptime timestamp must be updated with
+snmp_inc_sysuptime(). You should call this from a timer interrupt
+or a timer signal handler depending on your runtime environment.
+
+An alternative way to update the SNMP uptime timestamp is to do a call like
+snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to
+a lower frequency). Another one is to not call snmp_inc_sysuptime() or
+snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+when it's queried (any function which need "sysuptime" have to call
+snmp_get_sysuptime).
+
+
+3 Private MIBs
+==============
+
+If want to extend the agent with your own private MIB you'll need to
+add the following define to your local lwipopts.h:
+
+#define SNMP_PRIVATE_MIB 1
+
+You must provide the private_mib.h and associated files yourself.
+Note we don't have a "MIB compiler" that generates C source from a MIB,
+so you're required to do some serious coding if you enable this!
+
+Note the lwIP enterprise ID (26381) is assigned to the lwIP project,
+ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP
+MAINTAINERS!
+
+If you need to create your own private MIB you'll need
+to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html
+
+You can set it by passing a struct snmp_obj_id to the agent
+using snmp_set_sysobjid(&my_object_id), just before snmp_init().
+
+Note the object identifiers for thes MIB-2 and your private MIB
+tree must be kept in sorted ascending (lexicographical) order.
+This to ensure correct getnext operation.
+
+An example for a private MIB is part of the "minimal Unix" project:
+contrib/ports/unix/proj/minimal/lwip_prvmib.c
+
+The next chapter gives a more detailed description of the
+MIB-2 tree and the optional private MIB.
+
+4 The Gory Details
+==================
+
+4.0 Object identifiers and the MIB tree.
+
+We have three distinct parts for all object identifiers:
+
+The prefix
+ .iso.org.dod.internet
+
+the middle part
+ .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress
+
+and the index part
+ .1.192.168.0.1
+
+Objects located above the .internet hierarchy aren't supported.
+Currently only the .mgmt sub-tree is available and
+when the SNMP_PRIVATE_MIB is enabled the .private tree
+becomes available too.
+
+Object identifiers from incoming requests are checked
+for a matching prefix, middle part and index part
+or are expanded(*) for GetNext requests with short
+or inexisting names in the request.
+(* we call this "expansion" but this also
+resembles the "auto-completion" operation)
+
+The middle part is usually located in ROM (const)
+to preserve precious RAM on small microcontrollers.
+However RAM location is possible for a dynamically
+changing private tree.
+
+The index part is handled by functions which in
+turn use dynamically allocated index trees from RAM.
+These trees are updated by e.g. the etharp code
+when new entries are made or removed form the ARP cache.
+
+/** @todo more gory details */
diff --git a/core/lwip/doc/sys_arch.txt b/core/lwip/doc/sys_arch.txt
new file mode 100644
index 00000000..4eb93078
--- /dev/null
+++ b/core/lwip/doc/sys_arch.txt
@@ -0,0 +1,216 @@
+sys_arch interface for lwIP 0.6++
+
+Author: Adam Dunkels
+
+The operating system emulation layer provides a common interface
+between the lwIP code and the underlying operating system kernel. The
+general idea is that porting lwIP to new architectures requires only
+small changes to a few header files and a new sys_arch
+implementation. It is also possible to do a sys_arch implementation
+that does not rely on any underlying operating system.
+
+The sys_arch provides semaphores and mailboxes to lwIP. For the full
+lwIP functionality, multiple threads support can be implemented in the
+sys_arch, but this is not required for the basic lwIP
+functionality. Previous versions of lwIP required the sys_arch to
+implement timer scheduling as well but as of lwIP 0.5 this is
+implemented in a higher layer.
+
+In addition to the source file providing the functionality of sys_arch,
+the OS emulation layer must provide several header files defining
+macros used throughout lwip. The files required and the macros they
+must define are listed below the sys_arch description.
+
+Semaphores can be either counting or binary - lwIP works with both
+kinds. Mailboxes are used for message passing and can be implemented
+either as a queue which allows multiple messages to be posted to a
+mailbox, or as a rendez-vous point where only one message can be
+posted at a time. lwIP works with both kinds, but the former type will
+be more efficient. A message in a mailbox is just a pointer, nothing
+more.
+
+Semaphores are represented by the type "sys_sem_t" which is typedef'd
+in the sys_arch.h file. Mailboxes are equivalently represented by the
+type "sys_mbox_t". lwIP does not place any restrictions on how
+sys_sem_t or sys_mbox_t are represented internally.
+
+The following functions must be implemented by the sys_arch:
+
+- void sys_init(void)
+
+ Is called to initialize the sys_arch layer.
+
+- sys_sem_t sys_sem_new(u8_t count)
+
+ Creates and returns a new semaphore. The "count" argument specifies
+ the initial state of the semaphore.
+
+- void sys_sem_free(sys_sem_t sem)
+
+ Deallocates a semaphore.
+
+- void sys_sem_signal(sys_sem_t sem)
+
+ Signals a semaphore.
+
+- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
+
+ Blocks the thread while waiting for the semaphore to be
+ signaled. If the "timeout" argument is non-zero, the thread should
+ only be blocked for the specified time (measured in
+ milliseconds). If the "timeout" argument is zero, the thread should be
+ blocked until the semaphore is signalled.
+
+ If the timeout argument is non-zero, the return value is the number of
+ milliseconds spent waiting for the semaphore to be signaled. If the
+ semaphore wasn't signaled within the specified time, the return value is
+ SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+ (i.e., it was already signaled), the function may return zero.
+
+ Notice that lwIP implements a function with a similar name,
+ sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+
+- sys_mbox_t sys_mbox_new(int size)
+
+ Creates an empty mailbox for maximum "size" elements. Elements stored
+ in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
+ in your lwipopts.h, or ignore this parameter in your implementation
+ and use a default size.
+
+- void sys_mbox_free(sys_mbox_t mbox)
+
+ Deallocates a mailbox. If there are messages still present in the
+ mailbox when the mailbox is deallocated, it is an indication of a
+ programming error in lwIP and the developer should be notified.
+
+- void sys_mbox_post(sys_mbox_t mbox, void *msg)
+
+ Posts the "msg" to the mailbox. This function have to block until
+ the "msg" is really posted.
+
+- err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg)
+
+ Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
+ is full, else, ERR_OK if the "msg" is posted.
+
+- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout)
+
+ Blocks the thread until a message arrives in the mailbox, but does
+ not block the thread longer than "timeout" milliseconds (similar to
+ the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
+ be blocked until a message arrives. The "msg" argument is a result
+ parameter that is set by the function (i.e., by doing "*msg =
+ ptr"). The "msg" parameter maybe NULL to indicate that the message
+ should be dropped.
+
+ The return values are the same as for the sys_arch_sem_wait() function:
+ Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+ timeout.
+
+ Note that a function with a similar name, sys_mbox_fetch(), is
+ implemented by lwIP.
+
+- u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg)
+
+ This is similar to sys_arch_mbox_fetch, however if a message is not
+ present in the mailbox, it immediately returns with the code
+ SYS_MBOX_EMPTY. On success 0 is returned.
+
+ To allow for efficient implementations, this can be defined as a
+ function-like macro in sys_arch.h instead of a normal function. For
+ example, a naive implementation could be:
+ #define sys_arch_mbox_tryfetch(mbox,msg) \
+ sys_arch_mbox_fetch(mbox,msg,1)
+ although this would introduce unnecessary delays.
+
+If threads are supported by the underlying operating system and if
+such functionality is needed in lwIP, the following function will have
+to be implemented as well:
+
+- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
+
+ Starts a new thread named "name" with priority "prio" that will begin its
+ execution in the function "thread()". The "arg" argument will be passed as an
+ argument to the thread() function. The stack size to used for this thread is
+ the "stacksize" parameter. The id of the new thread is returned. Both the id
+ and the priority are system dependent.
+
+- sys_prot_t sys_arch_protect(void)
+
+ This optional function does a "fast" critical region protection and returns
+ the previous protection level. This function is only called during very short
+ critical regions. An embedded system which supports ISR-based drivers might
+ want to implement this function by disabling interrupts. Task-based systems
+ might want to implement this by using a mutex or disabling tasking. This
+ function should support recursive calls from the same task or interrupt. In
+ other words, sys_arch_protect() could be called while already protected. In
+ that case the return value indicates that it is already protected.
+
+ sys_arch_protect() is only required if your port is supporting an operating
+ system.
+
+- void sys_arch_unprotect(sys_prot_t pval)
+
+ This optional function does a "fast" set of critical region protection to the
+ value specified by pval. See the documentation for sys_arch_protect() for
+ more information. This function is only required if your port is supporting
+ an operating system.
+
+Note:
+
+Be carefull with using mem_malloc() in sys_arch. When malloc() refers to
+mem_malloc() you can run into a circular function call problem. In mem.c
+lwip_mem_init() tries to allcate a semaphore using mem_malloc, which of course
+can't be performed when sys_arch uses mem_malloc.
+
+-------------------------------------------------------------------------------
+Additional files required for the "OS support" emulation layer:
+-------------------------------------------------------------------------------
+
+cc.h - Architecture environment, some compiler specific, some
+ environment specific (probably should move env stuff
+ to sys_arch.h.)
+
+ Typedefs for the types used by lwip -
+ u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
+
+ Compiler hints for packing lwip's structures -
+ PACK_STRUCT_FIELD(x)
+ PACK_STRUCT_STRUCT
+ PACK_STRUCT_BEGIN
+ PACK_STRUCT_END
+
+ Platform specific diagnostic output -
+ LWIP_PLATFORM_DIAG(x) - non-fatal, print a message.
+ LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution.
+ Portability defines for printf formatters:
+ U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
+
+ "lightweight" synchronization mechanisms -
+ SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
+ SYS_ARCH_PROTECT(x) - enter protection mode.
+ SYS_ARCH_UNPROTECT(x) - leave protection mode.
+
+ If the compiler does not provide memset() this file must include a
+ definition of it, or include a file which defines it.
+
+ This file must either include a system-local <errno.h> which defines
+ the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO
+ to make lwip/arch.h define the codes which are used throughout.
+
+
+perf.h - Architecture specific performance measurement.
+ Measurement calls made throughout lwip, these can be defined to nothing.
+ PERF_START - start measuring something.
+ PERF_STOP(x) - stop measuring something, and record the result.
+
+sys_arch.h - Tied to sys_arch.c
+
+ Arch dependent types for the following objects:
+ sys_sem_t, sys_mbox_t, sys_thread_t,
+ And, optionally:
+ sys_prot_t
+
+ Defines to set vars of sys_mbox_t and sys_sem_t to NULL.
+ SYS_MBOX_NULL NULL
+ SYS_SEM_NULL NULL
diff --git a/core/lwip/src/FILES b/core/lwip/src/FILES
new file mode 100644
index 00000000..952aeabb
--- /dev/null
+++ b/core/lwip/src/FILES
@@ -0,0 +1,13 @@
+api/ - The code for the high-level wrapper API. Not needed if
+ you use the lowel-level call-back/raw API.
+
+core/ - The core of the TPC/IP stack; protocol implementations,
+ memory and buffer management, and the low-level raw API.
+
+include/ - lwIP include files.
+
+netif/ - Generic network interface device drivers are kept here,
+ as well as the ARP module.
+
+For more information on the various subdirectories, check the FILES
+file in each directory.
diff --git a/core/lwip/src/api/api_lib.c b/core/lwip/src/api/api_lib.c
new file mode 100644
index 00000000..b1a9e525
--- /dev/null
+++ b/core/lwip/src/api/api_lib.c
@@ -0,0 +1,740 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* This is the part of the API that is linked with
+ the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/tcpip.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+
+#include <string.h>
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+ struct netconn *conn;
+ struct api_msg msg;
+
+ conn = netconn_alloc(t, callback);
+ if (conn != NULL) {
+ msg.function = do_newconn;
+ msg.msg.msg.n.proto = proto;
+ msg.msg.conn = conn;
+ if (TCPIP_APIMSG(&msg) != ERR_OK) {
+ LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+ LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+ sys_sem_free(&conn->op_completed);
+ sys_mbox_free(&conn->recvmbox);
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+ }
+ return conn;
+}
+
+/**
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+ struct api_msg msg;
+
+ /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+
+ msg.function = do_delconn;
+ msg.msg.conn = conn;
+ tcpip_apimsg(&msg);
+
+ netconn_free(conn);
+
+ /* don't care for return value of do_delconn since it only calls void functions */
+
+ return ERR_OK;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ * ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+ msg.function = do_getaddr;
+ msg.msg.conn = conn;
+ msg.msg.msg.ad.ipaddr = addr;
+ msg.msg.msg.ad.port = port;
+ msg.msg.msg.ad.local = local;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY
+ * to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_bind;
+ msg.msg.conn = conn;
+ msg.msg.msg.bc.ipaddr = addr;
+ msg.msg.msg.bc.port = port;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_connect;
+ msg.msg.conn = conn;
+ msg.msg.msg.bc.ipaddr = addr;
+ msg.msg.msg.bc.port = port;
+ /* This is the only function which need to not block tcpip_thread */
+ err = tcpip_apimsg(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return TODO: return value is not set here...
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_disconnect;
+ msg.msg.conn = conn;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ * don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+ struct api_msg msg;
+ err_t err;
+
+ /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+ LWIP_UNUSED_ARG(backlog);
+
+ LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_listen;
+ msg.msg.conn = conn;
+#if TCP_LISTEN_BACKLOG
+ msg.msg.msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(backlog);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ * code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+ struct netconn *newconn;
+ err_t err;
+#if TCP_LISTEN_BACKLOG
+ struct api_msg msg;
+#endif /* TCP_LISTEN_BACKLOG */
+
+ LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
+ *new_conn = NULL;
+ LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on acceptmbox forever! */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+ if (newconn == NULL) {
+ /* connection has been aborted */
+ NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
+ return ERR_ABRT;
+ }
+#if TCP_LISTEN_BACKLOG
+ /* Let the stack know that we have accepted the connection. */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ *new_conn = newconn;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(new_conn);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf)
+{
+ void *buf = NULL;
+ u16_t len;
+ err_t err;
+#if LWIP_TCP
+ struct api_msg msg;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on recvmbox forever! */
+ /* @todo: this does not allow us to fetch data that has been put into recvmbox
+ before the fatal error occurred - is that a problem? */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ if (buf != NULL) {
+ msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
+ } else {
+ msg.msg.msg.r.len = 1;
+ }
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+ }
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ /* Avoid to lose any previous error code */
+ NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+ return ERR_CLSD;
+ }
+ len = ((struct pbuf *)buf)->tot_len;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ len = netbuf_len((struct netbuf *)buf);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+}
+
+/**
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+ netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf);
+}
+
+/**
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+ struct netbuf *buf = NULL;
+ err_t err;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ struct pbuf *p = NULL;
+ /* This is not a listening netconn, since recvmbox is set */
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
+ return ERR_MEM;
+ }
+
+ err = netconn_recv_data(conn, (void **)&p);
+ if (err != ERR_OK) {
+ memp_free(MEMP_NETBUF, buf);
+ return err;
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ buf->p = p;
+ buf->ptr = p;
+ buf->port = 0;
+ ip_addr_set_any(&buf->addr);
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+ } else
+#endif /* LWIP_TCP */
+ {
+#if (LWIP_UDP || LWIP_RAW)
+ return netconn_recv_data(conn, (void **)new_buf);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+}
+
+/**
+ * TCP: update the receive window: by calling this, the application
+ * tells the stack that it has processed data and is able to accept
+ * new data.
+ * ATTENTION: use with care, this is mainly used for sockets!
+ * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
+ *
+ * @param conn the netconn for which to update the receive window
+ * @param length amount of data processed (ATTENTION: this must be accurate!)
+ */
+void
+netconn_recved(struct netconn *conn, u32_t length)
+{
+#if LWIP_TCP
+ if ((conn != NULL) && (conn->type == NETCONN_TCP) &&
+ (netconn_get_noautorecved(conn))) {
+ struct api_msg msg;
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ msg.msg.msg.r.len = length;
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+ }
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(length);
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
+{
+ if (buf != NULL) {
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+ return netconn_send(conn, buf);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+ msg.function = do_send;
+ msg.msg.conn = conn;
+ msg.msg.msg.b = buf;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;);
+ if (size == 0) {
+ return ERR_OK;
+ }
+
+ /* @todo: for non-blocking write, check if 'size' would ever fit into
+ snd_queue or snd_buf */
+ msg.function = do_write;
+ msg.msg.conn = conn;
+ msg.msg.msg.w.dataptr = dataptr;
+ msg.msg.msg.w.apiflags = apiflags;
+ msg.msg.msg.w.len = size;
+ /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+ but if it is, this is done inside api_msg.c:do_write(), so we can use the
+ non-blocking version here. */
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close ot shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_close;
+ msg.msg.conn = conn;
+ /* shutting down both ends is the same as closing */
+ msg.msg.msg.sd.shut = how;
+ /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
+ don't use TCPIP_APIMSG here */
+ err = tcpip_apimsg(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+ /* shutting down both ends is the same as closing */
+ return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+ return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+}
+
+#if LWIP_IGMP
+/**
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ * the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+ ip_addr_t *multiaddr,
+ ip_addr_t *netif_addr,
+ enum netconn_igmp join_or_leave)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_join_leave_group;
+ msg.msg.conn = conn;
+ msg.msg.msg.jl.multiaddr = multiaddr;
+ msg.msg.msg.jl.netif_addr = netif_addr;
+ msg.msg.msg.jl.join_or_leave = join_or_leave;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @return ERR_OK: resolving succeeded
+ * ERR_MEM: memory error, try again later
+ * ERR_ARG: dns client not initialized or invalid hostname
+ * ERR_VAL: dns server response was invalid
+ */
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+{
+ struct dns_api_msg msg;
+ err_t err;
+ sys_sem_t sem;
+
+ LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+
+ err = sys_sem_new(&sem, 0);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ msg.name = name;
+ msg.addr = addr;
+ msg.err = &err;
+ msg.sem = &sem;
+
+ tcpip_callback(do_gethostbyname, &msg);
+ sys_sem_wait(&sem);
+ sys_sem_free(&sem);
+
+ return err;
+}
+#endif /* LWIP_DNS*/
+
+#endif /* LWIP_NETCONN */
diff --git a/core/lwip/src/api/api_msg.c b/core/lwip/src/api/api_msg.c
new file mode 100644
index 00000000..448f96dd
--- /dev/null
+++ b/core/lwip/src/api/api_msg.c
@@ -0,0 +1,1535 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/tcpip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+static err_t do_writemore(struct netconn *conn);
+static void do_close_internal(struct netconn *conn);
+#endif
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only references it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr)
+{
+ struct pbuf *q;
+ struct netbuf *buf;
+ struct netconn *conn;
+
+ LWIP_UNUSED_ARG(addr);
+ conn = (struct netconn *)arg;
+
+ if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+ return 0;
+ }
+#endif /* LWIP_SO_RCVBUF */
+ /* copy the whole packet into new pbufs */
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(q != NULL) {
+ if (pbuf_copy(q, p) != ERR_OK) {
+ pbuf_free(q);
+ q = NULL;
+ }
+ }
+
+ if (q != NULL) {
+ u16_t len;
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(q);
+ return 0;
+ }
+
+ buf->p = q;
+ buf->ptr = q;
+ ip_addr_copy(buf->addr, *ip_current_src_addr());
+ buf->port = pcb->protocol;
+
+ len = q->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return 0;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+ }
+ }
+
+ return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr, u16_t port)
+{
+ struct netbuf *buf;
+ struct netconn *conn;
+ u16_t len;
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+ ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else /* LWIP_SO_RCVBUF */
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+ pbuf_free(p);
+ return;
+ }
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(p);
+ return;
+ } else {
+ buf->p = p;
+ buf->ptr = p;
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+ {
+ const struct ip_hdr* iphdr = ip_current_header();
+ /* get the UDP header - always in the first pbuf, ensured by udp_input */
+ const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr));
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+ buf->toport_chksum = udphdr->dest;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ }
+
+ len = p->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct netconn *conn;
+ u16_t len;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ /* recvmbox already deleted */
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ return ERR_OK;
+ }
+ /* Unlike for UDP or RAW pcbs, don't check for available space
+ using recv_avail since that could break the connection
+ (data is already ACKed) */
+
+ /* don't overwrite fatal errors! */
+ NETCONN_SET_SAFE_ERR(conn, err);
+
+ if (p != NULL) {
+ len = p->tot_len;
+ } else {
+ len = 0;
+ }
+
+ if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+ /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+ return ERR_MEM;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ do_writemore(conn);
+ } else if (conn->state == NETCONN_CLOSE) {
+ do_close_internal(conn);
+ }
+ /* @todo: implement connect timeout here? */
+
+ /* Did a nonblocking write fail before? Then check available write-space. */
+ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ do_writemore(conn);
+ } else if (conn->state == NETCONN_CLOSE) {
+ do_close_internal(conn);
+ }
+
+ if (conn) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+ struct netconn *conn;
+ enum netconn_state old_state;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ conn->pcb.tcp = NULL;
+
+ /* no check since this is always fatal! */
+ SYS_ARCH_PROTECT(lev);
+ conn->last_err = err;
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* reset conn->state now before waking up other threads */
+ old_state = conn->state;
+ conn->state = NETCONN_NONE;
+
+ /* Notify the user layer about a connection error. Used to signal
+ select. */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ /* Try to release selects pending on 'read' or 'write', too.
+ They will get an error if they actually try to read or write. */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ /* pass NULL-message to recvmbox to wake up pending recv */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ /* use trypost to prevent deadlock */
+ sys_mbox_trypost(&conn->recvmbox, NULL);
+ }
+ /* pass NULL-message to acceptmbox to wake up pending accept */
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ /* use trypost to preven deadlock */
+ sys_mbox_trypost(&conn->acceptmbox, NULL);
+ }
+
+ if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+ (old_state == NETCONN_CONNECT)) {
+ /* calling do_writemore/do_close_internal is not necessary
+ since the pcb has already been deleted! */
+ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+
+ if (!was_nonblocking_connect) {
+ /* set error return code */
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ /* wake up the waiting task */
+ sys_sem_signal(&conn->op_completed);
+ }
+ } else {
+ LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+ }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = conn->pcb.tcp;
+ tcp_arg(pcb, conn);
+ tcp_recv(pcb, recv_tcp);
+ tcp_sent(pcb, sent_tcp);
+ tcp_poll(pcb, poll_tcp, 4);
+ tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ struct netconn *newconn;
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+ if (!sys_mbox_valid(&conn->acceptmbox)) {
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+ return ERR_VAL;
+ }
+
+ /* We have to set the callback here even though
+ * the new socket is unknown. conn->socket is marked as -1. */
+ newconn = netconn_alloc(conn->type, conn->callback);
+ if (newconn == NULL) {
+ return ERR_MEM;
+ }
+ newconn->pcb.tcp = newpcb;
+ setup_tcp(newconn);
+ /* no protection: when creating the pcb, the netconn is not yet known
+ to the application thread */
+ newconn->last_err = err;
+
+ if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+ /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+ so do nothing here! */
+ newconn->pcb.tcp = NULL;
+ /* no need to drain since we know the recvmbox is empty. */
+ sys_mbox_free(&newconn->recvmbox);
+ sys_mbox_set_invalid(&newconn->recvmbox);
+ netconn_free(newconn);
+ return ERR_MEM;
+ } else {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ * @return msg->conn->err, but the return value is currently ignored
+ */
+static void
+pcb_new(struct api_msg_msg *msg)
+{
+ LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+ /* Allocate a PCB for this connection */
+ switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
+ if(msg->conn->pcb.raw == NULL) {
+ msg->err = ERR_MEM;
+ break;
+ }
+ raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp = udp_new();
+ if(msg->conn->pcb.udp == NULL) {
+ msg->err = ERR_MEM;
+ break;
+ }
+#if LWIP_UDPLITE
+ if (msg->conn->type==NETCONN_UDPLITE) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+ }
+#endif /* LWIP_UDPLITE */
+ if (msg->conn->type==NETCONN_UDPNOCHKSUM) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->conn->pcb.tcp = tcp_new();
+ if(msg->conn->pcb.tcp == NULL) {
+ msg->err = ERR_MEM;
+ break;
+ }
+ setup_tcp(msg->conn);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ /* Unsupported netconn type, e.g. protocol disabled */
+ msg->err = ERR_VAL;
+ break;
+ }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param msg the api_msg_msg describing the connection type
+ */
+void
+do_newconn(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if(msg->conn->pcb.tcp == NULL) {
+ pcb_new(msg);
+ }
+ /* Else? This "new" connection already has a PCB allocated. */
+ /* Is this an error condition? Should it be deleted? */
+ /* We currently just are happy and return. */
+
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+ struct netconn *conn;
+ int size;
+
+ conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->last_err = ERR_OK;
+ conn->type = t;
+ conn->pcb.tcp = NULL;
+
+#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \
+ (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE)
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+#else
+ switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ size = DEFAULT_UDP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ size = DEFAULT_TCP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+ break;
+ }
+#endif
+
+ if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+ if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+ sys_sem_free(&conn->op_completed);
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+
+#if LWIP_TCP
+ sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+ conn->state = NETCONN_NONE;
+#if LWIP_SOCKET
+ /* initialize socket to -1 since 0 is a valid socket */
+ conn->socket = -1;
+#endif /* LWIP_SOCKET */
+ conn->callback = callback;
+#if LWIP_TCP
+ conn->current_msg = NULL;
+ conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_RCVTIMEO
+ conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+ conn->recv_avail = 0;
+#endif /* LWIP_SO_RCVBUF */
+ conn->flags = 0;
+ return conn;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+ LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+ sys_sem_free(&conn->op_completed);
+ sys_sem_set_invalid(&conn->op_completed);
+
+ memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+ void *mem;
+#if LWIP_TCP
+ struct pbuf *p;
+#endif /* LWIP_TCP */
+
+ /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+ /* Delete and drain the recvmbox. */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ if(mem != NULL) {
+ p = (struct pbuf*)mem;
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_recved(conn->pcb.tcp, p->tot_len);
+ }
+ pbuf_free(p);
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ netbuf_delete((struct netbuf *)mem);
+ }
+ }
+ sys_mbox_free(&conn->recvmbox);
+ sys_mbox_set_invalid(&conn->recvmbox);
+ }
+
+ /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+ struct netconn *newconn = (struct netconn *)mem;
+ /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_accepted(conn->pcb.tcp);
+ }
+ /* drain recvmbox */
+ netconn_drain(newconn);
+ if (newconn->pcb.tcp != NULL) {
+ tcp_abort(newconn->pcb.tcp);
+ newconn->pcb.tcp = NULL;
+ }
+ netconn_free(newconn);
+ }
+ sys_mbox_free(&conn->acceptmbox);
+ sys_mbox_set_invalid(&conn->acceptmbox);
+ }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static void
+do_close_internal(struct netconn *conn)
+{
+ err_t err;
+ u8_t shut, shut_rx, shut_tx, close;
+
+ LWIP_ASSERT("invalid conn", (conn != NULL));
+ LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
+ LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+ LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+ shut = conn->current_msg->msg.sd.shut;
+ shut_rx = shut & NETCONN_SHUT_RD;
+ shut_tx = shut & NETCONN_SHUT_WR;
+ /* shutting down both ends is the same as closing */
+ close = shut == NETCONN_SHUT_RDWR;
+
+ /* Set back some callback pointers */
+ if (close) {
+ tcp_arg(conn->pcb.tcp, NULL);
+ }
+ if (conn->pcb.tcp->state == LISTEN) {
+ tcp_accept(conn->pcb.tcp, NULL);
+ } else {
+ /* some callbacks have to be reset if tcp_close is not successful */
+ if (shut_rx) {
+ tcp_recv(conn->pcb.tcp, NULL);
+ tcp_accept(conn->pcb.tcp, NULL);
+ }
+ if (shut_tx) {
+ tcp_sent(conn->pcb.tcp, NULL);
+ }
+ if (close) {
+ tcp_poll(conn->pcb.tcp, NULL, 4);
+ tcp_err(conn->pcb.tcp, NULL);
+ }
+ }
+ /* Try to close the connection */
+ if (shut == NETCONN_SHUT_RDWR) {
+ err = tcp_close(conn->pcb.tcp);
+ } else {
+ err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR);
+ }
+ if (err == ERR_OK) {
+ /* Closing succeeded */
+ conn->current_msg->err = ERR_OK;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ /* Set back some callback pointers as conn is going away */
+ conn->pcb.tcp = NULL;
+ /* Trigger select() in socket layer. Make sure everybody notices activity
+ on the connection, error first! */
+ if (close) {
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ }
+ if (shut_rx) {
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ if (shut_tx) {
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ /* wake up the application task */
+ sys_sem_signal(&conn->op_completed);
+ } else {
+ /* Closing failed, restore some of the callbacks */
+ /* Closing of listen pcb will never fail! */
+ LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
+ tcp_sent(conn->pcb.tcp, sent_tcp);
+ tcp_poll(conn->pcb.tcp, poll_tcp, 4);
+ tcp_err(conn->pcb.tcp, err_tcp);
+ tcp_arg(conn->pcb.tcp, conn);
+ /* don't restore recv callback: we don't want to receive any more data */
+ }
+ /* If closing didn't succeed, we get called again either
+ from poll_tcp or from sent_tcp */
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_delconn(struct api_msg_msg *msg)
+{
+ /* @todo TCP: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) &&
+ (msg->conn->state != NETCONN_LISTEN) &&
+ (msg->conn->state != NETCONN_CONNECT)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else {
+ LWIP_ASSERT("blocking connect in progress",
+ (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+
+ if (msg->conn->pcb.tcp != NULL) {
+
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_remove(msg->conn->pcb.raw);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp->recv_arg = NULL;
+ udp_remove(msg->conn->pcb.udp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+ msg->conn->current_msg = msg;
+ do_close_internal(msg->conn);
+ /* API_EVENT is called inside do_close_internal, before releasing
+ the application thread, so we can return at this point! */
+ return;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ msg->conn->pcb.tcp = NULL;
+ }
+ /* tcp netconns don't come here! */
+
+ /* @todo: this lets select make the socket readable and writable,
+ which is wrong! errfd instead? */
+ API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ if (sys_sem_valid(&msg->conn->op_completed)) {
+ sys_sem_signal(&msg->conn->op_completed);
+ }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+do_bind(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_VAL;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct netconn *conn;
+ int was_blocking;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+ LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+ (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+ if (conn->current_msg != NULL) {
+ conn->current_msg->err = err;
+ }
+ if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) {
+ setup_tcp(conn);
+ }
+ was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ if (!was_blocking) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+ }
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ if (was_blocking) {
+ sys_sem_signal(&conn->op_completed);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to connect to
+ */
+void
+do_connect(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.tcp == NULL) {
+ /* This may happen when calling netconn_connect() a second time */
+ msg->err = ERR_CLSD;
+ } else {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state != NETCONN_NONE) {
+ msg->err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
+ msg->msg.bc.port, do_connected);
+ if (msg->err == ERR_OK) {
+ u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+ msg->conn->state = NETCONN_CONNECT;
+ SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+ if (non_blocking) {
+ msg->err = ERR_INPROGRESS;
+ } else {
+ msg->conn->current_msg = msg;
+ /* sys_sem_signal() is called from do_connected (or err_tcp()),
+ * when the connection is established! */
+ return;
+ }
+ }
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+ break;
+ }
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param msg the api_msg_msg pointing to the connection to disconnect
+ */
+void
+do_disconnect(struct api_msg_msg *msg)
+{
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ udp_disconnect(msg->conn->pcb.udp);
+ msg->err = ERR_OK;
+ } else
+#endif /* LWIP_UDP */
+ {
+ msg->err = ERR_VAL;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_listen(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (msg->conn->type == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+#if TCP_LISTEN_BACKLOG
+ struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+#else /* TCP_LISTEN_BACKLOG */
+ struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp);
+#endif /* TCP_LISTEN_BACKLOG */
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ msg->err = ERR_MEM;
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ msg->err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (msg->err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
+ } else {
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_send(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ }
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ } else {
+ msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ &msg->msg.b->addr, msg->msg.b->port,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ }
+#else /* LWIP_CHECKSUM_ON_COPY */
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_recv(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (msg->conn->type == NETCONN_TCP) {
+#if TCP_LISTEN_BACKLOG
+ if (msg->conn->pcb.tcp->state == LISTEN) {
+ tcp_accepted(msg->conn->pcb.tcp);
+ } else
+#endif /* TCP_LISTEN_BACKLOG */
+ {
+ u32_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ }while(remaining != 0);
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+do_writemore(struct netconn *conn)
+{
+ err_t err = ERR_OK;
+ void *dataptr;
+ u16_t len, available;
+ u8_t write_finished = 0;
+ size_t diff;
+ u8_t dontblock = netconn_is_nonblocking(conn) ||
+ (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
+ u8_t apiflags = conn->current_msg->msg.w.apiflags;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+ LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+ conn->write_offset < conn->current_msg->msg.w.len);
+
+ dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+ diff = conn->current_msg->msg.w.len - conn->write_offset;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ len = (u16_t)diff;
+ }
+ available = tcp_sndbuf(conn->pcb.tcp);
+ if (available < len) {
+ /* don't try to write more than sendbuf */
+ len = available;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ if (dontblock && (len < conn->current_msg->msg.w.len)) {
+ /* failed to send all data at once -> nonblocking write not possible */
+ err = ERR_MEM;
+ }
+ if (err == ERR_OK) {
+ LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ }
+ if (dontblock && (err == ERR_MEM)) {
+ /* nonblocking write failed */
+ write_finished = 1;
+ err = ERR_WOULDBLOCK;
+ /* let poll_tcp check writable space to mark the pcb
+ writable again */
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ /* let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ } else {
+ /* if OK or memory error, check available space */
+ if (((err == ERR_OK) || (err == ERR_MEM)) &&
+ ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ }
+
+ if (err == ERR_OK) {
+ conn->write_offset += len;
+ if (conn->write_offset == conn->current_msg->msg.w.len) {
+ /* everything was written */
+ write_finished = 1;
+ conn->write_offset = 0;
+ }
+ tcp_output(conn->pcb.tcp);
+ } else if (err == ERR_MEM) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
+ we do NOT return to the application thread, since ERR_MEM is
+ only a temporary error! */
+
+ /* tcp_write returned ERR_MEM, try tcp_output anyway */
+ tcp_output(conn->pcb.tcp);
+
+ #if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+ #endif
+ } else {
+ /* On errors != ERR_MEM, we don't try writing any more but return
+ the error to the application thread. */
+ write_finished = 1;
+ }
+ }
+
+ if (write_finished) {
+ /* everything was written: set back connection state
+ and back to application task */
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+#if LWIP_TCPIP_CORE_LOCKING
+ if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0)
+#endif
+ {
+ sys_sem_signal(&conn->op_completed);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ else
+ return ERR_MEM;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_write(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->type == NETCONN_TCP) {
+#if LWIP_TCP
+ if (msg->conn->state != NETCONN_NONE) {
+ /* netconn is connecting, closing or in blocking write */
+ msg->err = ERR_INPROGRESS;
+ } else if (msg->conn->pcb.tcp != NULL) {
+ msg->conn->state = NETCONN_WRITE;
+ /* set all the variables used by do_writemore */
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+ msg->conn->current_msg = msg;
+ msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+ msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED;
+ if (do_writemore(msg->conn) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(&msg->conn->op_completed, 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for both cases: if do_writemore was called, don't ACK the APIMSG
+ since do_writemore ACKs it! */
+ return;
+ } else {
+ msg->err = ERR_CONN;
+ }
+#else /* LWIP_TCP */
+ msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_getaddr(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.ip != NULL) {
+ *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip :
+ msg->conn->pcb.ip->remote_ip);
+
+ msg->err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+ } else {
+ /* return an error as connecting is only a helper for upper layers */
+ msg->err = ERR_CONN;
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+ } else {
+ if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+ msg->err = ERR_CONN;
+ } else {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ }
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close a TCP pcb contained in a netconn
+ * Called from netconn_close
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_close(struct api_msg_msg *msg)
+{
+#if LWIP_TCP
+ /* @todo: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
+ if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
+ /* LISTEN doesn't support half shutdown */
+ msg->err = ERR_CONN;
+ } else {
+ if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+ }
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->conn->current_msg = msg;
+ do_close_internal(msg->conn);
+ /* for tcp netconns, do_close_internal ACKs the message */
+ return;
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ msg->err = ERR_VAL;
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+#if LWIP_IGMP
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_join_leave_group(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
+ } else {
+ msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
+ LWIP_UNUSED_ARG(name);
+
+ if (ipaddr == NULL) {
+ /* timeout or memory error */
+ *msg->err = ERR_VAL;
+ } else {
+ /* address was resolved */
+ *msg->err = ERR_OK;
+ *msg->addr = *ipaddr;
+ }
+ /* wake up the application task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+do_gethostbyname(void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg);
+ if (*msg->err != ERR_INPROGRESS) {
+ /* on error or immediate success, wake up the application
+ * task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+ }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/core/lwip/src/api/err.c b/core/lwip/src/api/err.c
new file mode 100644
index 00000000..92fa8b7d
--- /dev/null
+++ b/core/lwip/src/api/err.c
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+ "Ok.", /* ERR_OK 0 */
+ "Out of memory error.", /* ERR_MEM -1 */
+ "Buffer error.", /* ERR_BUF -2 */
+ "Timeout.", /* ERR_TIMEOUT -3 */
+ "Routing problem.", /* ERR_RTE -4 */
+ "Operation in progress.", /* ERR_INPROGRESS -5 */
+ "Illegal value.", /* ERR_VAL -6 */
+ "Operation would block.", /* ERR_WOULDBLOCK -7 */
+ "Address in use.", /* ERR_USE -8 */
+ "Already connected.", /* ERR_ISCONN -9 */
+ "Connection aborted.", /* ERR_ABRT -10 */
+ "Connection reset.", /* ERR_RST -11 */
+ "Connection closed.", /* ERR_CLSD -12 */
+ "Not connected.", /* ERR_CONN -13 */
+ "Illegal argument.", /* ERR_ARG -14 */
+ "Low-level netif error.", /* ERR_IF -15 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+ return err_strerr[-err];
+
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/core/lwip/src/api/netbuf.c b/core/lwip/src/api/netbuf.c
new file mode 100644
index 00000000..9390c9ee
--- /dev/null
+++ b/core/lwip/src/api/netbuf.c
@@ -0,0 +1,245 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ * NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+ struct netbuf *buf;
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf != NULL) {
+ buf->p = NULL;
+ buf->ptr = NULL;
+ ip_addr_set_any(&buf->addr);
+ buf->port = 0;
+#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ buf->toport_chksum = 0;
+#if LWIP_NETBUF_RECVINFO
+ ip_addr_set_any(&buf->toaddr);
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+ return buf;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+ if (buf != NULL) {
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ buf->p = buf->ptr = NULL;
+ }
+ memp_free(MEMP_NETBUF, buf);
+ }
+}
+
+/**
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ * NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+ LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+ /* Deallocate any previously allocated memory. */
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+ if (buf->p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("check that first pbuf can hold size",
+ (buf->p->len >= size));
+ buf->ptr = buf->p;
+ return buf->p->payload;
+}
+
+/**
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = buf->ptr = NULL;
+}
+
+/**
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ * ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+ LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (buf->p == NULL) {
+ buf->ptr = NULL;
+ return ERR_MEM;
+ }
+ buf->p->payload = (void*)dataptr;
+ buf->p->len = buf->p->tot_len = size;
+ buf->ptr = buf->p;
+ return ERR_OK;
+}
+
+/**
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+ LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;);
+ LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+ pbuf_cat(head->p, tail->p);
+ head->ptr = head->p;
+ memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retreived,
+ * ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+ LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+ if (buf->ptr == NULL) {
+ return ERR_BUF;
+ }
+ *dataptr = buf->ptr->payload;
+ *len = buf->ptr->len;
+ return ERR_OK;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ * 1 if moved to the next part but now there is no next part
+ * 0 if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;);
+ if (buf->ptr->next == NULL) {
+ return -1;
+ }
+ buf->ptr = buf->ptr->next;
+ if (buf->ptr->next == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/core/lwip/src/api/netdb.c b/core/lwip/src/api/netdb.c
new file mode 100644
index 00000000..a7e4e06b
--- /dev/null
+++ b/core/lwip/src/api/netdb.c
@@ -0,0 +1,352 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+ ip_addr_t *addrs;
+ ip_addr_t addr;
+ char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ * for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+ err_t err;
+ ip_addr_t addr;
+
+ /* buffer variables for lwip_gethostbyname() */
+ HOSTENT_STORAGE struct hostent s_hostent;
+ HOSTENT_STORAGE char *s_aliases;
+ HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+ HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ /* fill hostent */
+ s_hostent_addr = addr;
+ s_phostent_addr[0] = &s_hostent_addr;
+ s_phostent_addr[1] = NULL;
+ s_hostent.h_name = (char*)name;
+ s_hostent.h_aliases = &s_aliases;
+ s_hostent.h_addrtype = AF_INET;
+ s_hostent.h_length = sizeof(ip_addr_t);
+ s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+ /* dump hostent */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases));
+ if (s_hostent.h_aliases != NULL) {
+ u8_t idx;
+ for ( idx=0; s_hostent.h_aliases[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx]));
+ }
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list));
+ if (s_hostent.h_addr_list != NULL) {
+ u8_t idx;
+ for ( idx=0; s_hostent.h_addr_list[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+ }
+ }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+ /* this function should return the "per-thread" hostent after copy from s_hostent */
+ return sys_thread_hostent(&s_hostent);
+#else
+ return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ * and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ * the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ * is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop)
+{
+ err_t err;
+ struct gethostbyname_r_helper *h;
+ char *hostname;
+ size_t namelen;
+ int lh_errno;
+
+ if (h_errnop == NULL) {
+ /* ensure h_errnop is never NULL */
+ h_errnop = &lh_errno;
+ }
+
+ if (result == NULL) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+ /* first thing to do: set *result to nothing */
+ *result = NULL;
+ if ((name == NULL) || (ret == NULL) || (buf == 0)) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+
+ namelen = strlen(name);
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+ /* buf can't hold the data needed + a copy of name */
+ *h_errnop = ERANGE;
+ return -1;
+ }
+
+ h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+ hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &(h->addr));
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ *h_errnop = ENSRNOTFOUND;
+ return -1;
+ }
+
+ /* copy the hostname into buf */
+ MEMCPY(hostname, name, namelen);
+ hostname[namelen] = 0;
+
+ /* fill hostent */
+ h->addrs = &(h->addr);
+ h->aliases = NULL;
+ ret->h_name = (char*)hostname;
+ ret->h_aliases = &(h->aliases);
+ ret->h_addrtype = AF_INET;
+ ret->h_length = sizeof(ip_addr_t);
+ ret->h_addr_list = (char**)&(h->addrs);
+
+ /* set result != NULL */
+ *result = ret;
+
+ /* return success */
+ return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ while (ai != NULL) {
+ next = ai->ai_next;
+ memp_free(MEMP_NETDB, ai);
+ ai = next;
+ }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ * (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ err_t err;
+ ip_addr_t addr;
+ struct addrinfo *ai;
+ struct sockaddr_in *sa = NULL;
+ int port_nr = 0;
+ size_t total_size;
+ size_t namelen = 0;
+
+ if (res == NULL) {
+ return EAI_FAIL;
+ }
+ *res = NULL;
+ if ((nodename == NULL) && (servname == NULL)) {
+ return EAI_NONAME;
+ }
+
+ if (servname != NULL) {
+ /* service name specified: convert to port number
+ * @todo?: currently, only ASCII integers (port numbers) are supported! */
+ port_nr = atoi(servname);
+ if ((port_nr <= 0) || (port_nr > 0xffff)) {
+ return EAI_SERVICE;
+ }
+ }
+
+ if (nodename != NULL) {
+ /* service location specified, try to resolve */
+ err = netconn_gethostbyname(nodename, &addr);
+ if (err != ERR_OK) {
+ return EAI_FAIL;
+ }
+ } else {
+ /* service location specified, use loopback address */
+ ip_addr_set_loopback(&addr);
+ }
+
+ total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
+ if (nodename != NULL) {
+ namelen = strlen(nodename);
+ LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1);
+ total_size += namelen + 1;
+ }
+ /* If this fails, please report to lwip-devel! :-) */
+ LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+ total_size <= NETDB_ELEM_SIZE);
+ ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+ if (ai == NULL) {
+ goto memerr;
+ }
+ memset(ai, 0, total_size);
+ sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo));
+ /* set up sockaddr */
+ inet_addr_from_ipaddr(&sa->sin_addr, &addr);
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(struct sockaddr_in);
+ sa->sin_port = htons((u16_t)port_nr);
+
+ /* set up addrinfo */
+ ai->ai_family = AF_INET;
+ if (hints != NULL) {
+ /* copy socktype & protocol from hints if specified */
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ }
+ if (nodename != NULL) {
+ /* copy nodename to canonname if specified */
+ ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+ MEMCPY(ai->ai_canonname, nodename, namelen);
+ ai->ai_canonname[namelen] = 0;
+ }
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ ai->ai_addr = (struct sockaddr*)sa;
+
+ *res = ai;
+
+ return 0;
+memerr:
+ if (ai != NULL) {
+ memp_free(MEMP_NETDB, ai);
+ }
+ return EAI_MEMORY;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/core/lwip/src/api/netifapi.c b/core/lwip/src/api/netifapi.c
new file mode 100644
index 00000000..43e47203
--- /dev/null
+++ b/core/lwip/src/api/netifapi.c
@@ -0,0 +1,160 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/tcpip.h"
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+void
+do_netifapi_netif_add(struct netifapi_msg_msg *msg)
+{
+ if (!netif_add( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw,
+ msg->msg.add.state,
+ msg->msg.add.init,
+ msg->msg.add.input)) {
+ msg->err = ERR_IF;
+ } else {
+ msg->err = ERR_OK;
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+void
+do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg)
+{
+ netif_set_addr( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw);
+ msg->err = ERR_OK;
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+void
+do_netifapi_netif_common(struct netifapi_msg_msg *msg)
+{
+ if (msg->msg.common.errtfunc != NULL) {
+ msg->err = msg->msg.common.errtfunc(msg->netif);
+ } else {
+ msg->err = ERR_OK;
+ msg->msg.common.voidfunc(msg->netif);
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_add;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ msg.msg.msg.add.state = state;
+ msg.msg.msg.add.init = init;
+ msg.msg.msg.add.input = input;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_set_addr;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_common;
+ msg.msg.netif = netif;
+ msg.msg.msg.common.voidfunc = voidfunc;
+ msg.msg.msg.common.errtfunc = errtfunc;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/core/lwip/src/api/sockets.c b/core/lwip/src/api/sockets.c
new file mode 100644
index 00000000..e36012ce
--- /dev/null
+++ b/core/lwip/src/api/sockets.c
@@ -0,0 +1,2347 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcpip.h"
+#include "lwip/pbuf.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+ /** sockets currently are built on netconns, each socket has one netconn */
+ struct netconn *conn;
+ /** data that was left from the previous read */
+ void *lastdata;
+ /** offset in the data that was left from the previous read */
+ u16_t lastoffset;
+ /** number of times data was received, set by event_callback(),
+ tested by the receive and select functions */
+ s16_t rcvevent;
+ /** number of times data was ACKed (free send buffer), set by event_callback(),
+ tested by select */
+ u16_t sendevent;
+ /** error happened for this socket, set by event_callback(), tested by select */
+ u16_t errevent;
+ /** last error that occurred on this socket */
+ int err;
+ /** counter of how many threads are waiting for this socket using select */
+ int select_waiting;
+};
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+ /** Pointer to the next waiting task */
+ struct lwip_select_cb *next;
+ /** Pointer to the previous waiting task */
+ struct lwip_select_cb *prev;
+ /** readset passed to select */
+ fd_set *readset;
+ /** writeset passed to select */
+ fd_set *writeset;
+ /** unimplemented: exceptset passed to select */
+ fd_set *exceptset;
+ /** don't signal the same semaphore twice: set to 1 when signalled */
+ int sem_signalled;
+ /** semaphore to wake up a task waiting for select */
+ sys_sem_t sem;
+};
+
+/** This struct is used to pass data to the set/getsockopt_internal
+ * functions running in tcpip_thread context (only a void* is allowed) */
+struct lwip_setgetsockopt_data {
+ /** socket struct for which to change options */
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ /** socket index for which to change options */
+ int s;
+#endif /* LWIP_DEBUG */
+ /** level of the option to process */
+ int level;
+ /** name of the option to process */
+ int optname;
+ /** set: value to set the option to
+ * get: value of the option is stored here */
+ void *optval;
+ /** size of *optval */
+ socklen_t *optlen;
+ /** if an error occures, it is temporarily stored here */
+ err_t err;
+};
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+/** This counter is increased from lwip_select when the list is chagned
+ and checked in event_callback to see if it has changed. */
+static volatile int select_cb_ctr;
+
+/** Table to quickly map an lwIP error (err_t) to a socket error
+ * by using -err as an index */
+static const int err_to_errno_table[] = {
+ 0, /* ERR_OK 0 No error, everything OK. */
+ ENOMEM, /* ERR_MEM -1 Out of memory error. */
+ ENOBUFS, /* ERR_BUF -2 Buffer error. */
+ EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
+ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
+ EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
+ EINVAL, /* ERR_VAL -6 Illegal value. */
+ EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
+ EADDRINUSE, /* ERR_USE -8 Address in use. */
+ EALREADY, /* ERR_ISCONN -9 Already connected. */
+ ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
+ ECONNRESET, /* ERR_RST -11 Connection reset. */
+ ENOTCONN, /* ERR_CLSD -12 Connection closed. */
+ ENOTCONN, /* ERR_CONN -13 Not connected. */
+ EIO, /* ERR_ARG -14 Illegal argument. */
+ -1, /* ERR_IF -15 Low-level netif error */
+};
+
+#define ERR_TO_ERRNO_TABLE_SIZE \
+ (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
+
+#define err_to_errno(err) \
+ ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
+ err_to_errno_table[-(err)] : EIO)
+
+#ifdef ERRNO
+#ifndef set_errno
+#define set_errno(err) errno = (err)
+#endif
+#else /* ERRNO */
+#define set_errno(err)
+#endif /* ERRNO */
+
+#define sock_set_errno(sk, e) do { \
+ sk->err = (e); \
+ set_errno(sk->err); \
+} while (0)
+
+/* Forward delcaration of some functions */
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+static void lwip_getsockopt_internal(void *arg);
+static void lwip_setsockopt_internal(void *arg);
+
+/**
+ * Initialize this module. This function has to be called before any other
+ * functions in this module!
+ */
+void
+lwip_socket_init(void)
+{
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int s)
+{
+ struct lwip_sock *sock;
+
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ sock = &sockets[s];
+
+ if (!sock->conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ return sock;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int s)
+{
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ return NULL;
+ }
+ if (!sockets[s].conn) {
+ return NULL;
+ }
+ return &sockets[s];
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ * 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* allocate a new socket identifier */
+ for (i = 0; i < NUM_SOCKETS; ++i) {
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ if (!sockets[i].conn) {
+ sockets[i].conn = newconn;
+ /* The socket is not yet known to anyone, so no need to protect
+ after having marked it as used. */
+ SYS_ARCH_UNPROTECT(lev);
+ sockets[i].lastdata = NULL;
+ sockets[i].lastoffset = 0;
+ sockets[i].rcvevent = 0;
+ /* TCP sendbuf is empty, but the socket is not yet writable until connected
+ * (unless it has been created by accept()). */
+ sockets[i].sendevent = (newconn->type == NETCONN_TCP ? (accepted != 0) : 1);
+ sockets[i].errevent = 0;
+ sockets[i].err = 0;
+ sockets[i].select_waiting = 0;
+ return i;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ return -1;
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+ void *lastdata;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ lastdata = sock->lastdata;
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ sock->err = 0;
+
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ sock->conn = NULL;
+ SYS_ARCH_UNPROTECT(lev);
+ /* don't use 'sock' after this line, as another task might have allocated it */
+
+ if (lastdata != NULL) {
+ if (is_tcp) {
+ pbuf_free((struct pbuf *)lastdata);
+ } else {
+ netbuf_delete((struct netbuf *)lastdata);
+ }
+ }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ struct lwip_sock *sock, *nsock;
+ struct netconn *newconn;
+ ip_addr_t naddr;
+ u16_t port;
+ int newsock;
+ struct sockaddr_in sin;
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
+ sock_set_errno(sock, EWOULDBLOCK);
+ return -1;
+ }
+
+ /* wait for a new connection */
+ err = netconn_accept(sock->conn, &newconn);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ LWIP_ASSERT("newconn != NULL", newconn != NULL);
+ /* Prevent automatic window updates, we do this on our own! */
+ netconn_set_noautorecved(newconn, 1);
+
+ /* get the IP address and port of the remote host */
+ err = netconn_peer(newconn, &naddr, &port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+ netconn_delete(newconn);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+ * not be NULL if addr is valid.
+ */
+ if (NULL != addr) {
+ LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
+
+ if (*addrlen > sizeof(sin))
+ *addrlen = sizeof(sin);
+
+ MEMCPY(addr, &sin, *addrlen);
+ }
+
+ newsock = alloc_socket(newconn, 1);
+ if (newsock == -1) {
+ netconn_delete(newconn);
+ sock_set_errno(sock, ENFILE);
+ return -1;
+ }
+ LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));
+ LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
+ nsock = &sockets[newsock];
+
+ /* See event_callback: If data comes in right away after an accept, even
+ * though the server task might not have created a new socket yet.
+ * In that case, newconn->socket is counted down (newconn->socket--),
+ * so nsock->rcvevent is >= 1 here!
+ */
+ SYS_ARCH_PROTECT(lev);
+ nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+ newconn->socket = newsock;
+ SYS_ARCH_UNPROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+ ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+
+ sock_set_errno(sock, 0);
+ return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ ip_addr_t local_addr;
+ u16_t local_port;
+ err_t err;
+ const struct sockaddr_in *name_in;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* check size, familiy and alignment of 'name' */
+ LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) &&
+ ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ name_in = (const struct sockaddr_in *)(void*)name;
+
+ inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr);
+ local_port = name_in->sin_port;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, &local_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port)));
+
+ err = netconn_bind(sock->conn, &local_addr, ntohs(local_port));
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_close(int s)
+{
+ struct lwip_sock *sock;
+ int is_tcp = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if(sock->conn != NULL) {
+ is_tcp = netconn_type(sock->conn) == NETCONN_TCP;
+ } else {
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+ }
+
+ netconn_delete(sock->conn);
+
+ free_socket(sock, is_tcp);
+ set_errno(0);
+ return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ const struct sockaddr_in *name_in;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* check size, familiy and alignment of 'name' */
+ LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) &&
+ ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ name_in = (const struct sockaddr_in *)(void*)name;
+
+ if (name_in->sin_family == AF_UNSPEC) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+ err = netconn_disconnect(sock->conn);
+ } else {
+ ip_addr_t remote_addr;
+ u16_t remote_port;
+
+ inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr);
+ remote_port = name_in->sin_port;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port)));
+
+ err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port));
+ }
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* limit the "backlog" parameter to fit in an u8_t */
+ backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+ err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ void *buf = NULL;
+ struct pbuf *p;
+ u16_t buflen, copylen;
+ int off = 0;
+ ip_addr_t *addr;
+ u16_t port;
+ u8_t done = 0;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ do {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+ /* Check if there is data left from the last recv operation. */
+ if (sock->lastdata) {
+ buf = sock->lastdata;
+ } else {
+ /* If this is non-blocking call, then check first */
+ if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+ (sock->rcvevent <= 0)) {
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
+ sock_set_errno(sock, EWOULDBLOCK);
+ return -1;
+ }
+
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
+ } else {
+ err = netconn_recv(sock->conn, (struct netbuf **)&buf);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
+ err, buf));
+
+ if (err != ERR_OK) {
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ /* We should really do some error checking here. */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ if (err == ERR_CLSD) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata = buf;
+ }
+
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ p = (struct pbuf *)buf;
+ } else {
+ p = ((struct netbuf *)buf)->p;
+ }
+ buflen = p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
+ buflen, len, off, sock->lastoffset));
+
+ buflen -= sock->lastoffset;
+
+ if (len > buflen) {
+ copylen = buflen;
+ } else {
+ copylen = (u16_t)len;
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory pointer mem */
+ pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
+
+ off += copylen;
+
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
+ len -= copylen;
+ if ( (len <= 0) ||
+ (p->flags & PBUF_FLAG_PUSH) ||
+ (sock->rcvevent <= 0) ||
+ ((flags & MSG_PEEK)!=0)) {
+ done = 1;
+ }
+ } else {
+ done = 1;
+ }
+
+ /* Check to see from where the data was.*/
+ if (done) {
+ ip_addr_t fromaddr;
+ if (from && fromlen) {
+ struct sockaddr_in sin;
+
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ addr = &fromaddr;
+ netconn_getaddr(sock->conn, addr, &port, 0);
+ } else {
+ addr = netbuf_fromaddr((struct netbuf *)buf);
+ port = netbuf_fromport((struct netbuf *)buf);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ inet_addr_from_ipaddr(&sin.sin_addr, addr);
+
+ if (*fromlen > sizeof(sin)) {
+ *fromlen = sizeof(sin);
+ }
+
+ MEMCPY(from, &sin, *fromlen);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+ } else {
+#if SOCKETS_DEBUG
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ addr = &fromaddr;
+ netconn_getaddr(sock->conn, addr, &port, 0);
+ } else {
+ addr = netbuf_fromaddr((struct netbuf *)buf);
+ port = netbuf_fromport((struct netbuf *)buf);
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+#endif /* SOCKETS_DEBUG */
+ }
+ }
+
+ /* If we don't peek the incoming message... */
+ if ((flags & MSG_PEEK) == 0) {
+ /* If this is a TCP socket, check if there is data left in the
+ buffer. If so, it should be saved in the sock structure for next
+ time around. */
+ if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) {
+ sock->lastdata = buf;
+ sock->lastoffset += copylen;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+ } else {
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ pbuf_free((struct pbuf *)buf);
+ } else {
+ netbuf_delete((struct netbuf *)buf);
+ }
+ }
+ }
+ } while (!done);
+
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ }
+ sock_set_errno(sock, 0);
+ return off;
+}
+
+int
+lwip_read(int s, void *mem, size_t len)
+{
+ return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+ return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t write_flags;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+ s, data, size, flags));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn->type != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+
+ if ((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) {
+ if ((size > TCP_SND_BUF) || ((size / TCP_MSS) > TCP_SND_QUEUELEN)) {
+ /* too much data to ever send nonblocking! */
+ sock_set_errno(sock, EMSGSIZE);
+ return -1;
+ }
+ }
+
+ write_flags = NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+ err = netconn_write(sock->conn, data, size, write_flags);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size));
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? (int)size : -1);
+}
+
+int
+lwip_sendto(int s, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u16_t short_size;
+ const struct sockaddr_in *to_in;
+ u16_t remote_port;
+#if !LWIP_TCPIP_CORE_LOCKING
+ struct netbuf buf;
+#endif
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn->type == NETCONN_TCP) {
+#if LWIP_TCP
+ return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(flags);
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_TCP */
+ }
+
+ /* @todo: split into multiple sendto's? */
+ LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+ short_size = (u16_t)size;
+ LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+ ((tolen == sizeof(struct sockaddr_in)) &&
+ ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ to_in = (const struct sockaddr_in *)(void*)to;
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */
+ {
+ struct pbuf* p;
+ ip_addr_t *remote_addr;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM);
+ if (p != NULL) {
+#if LWIP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ if (sock->conn->type != NETCONN_RAW) {
+ chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ MEMCPY(p->payload, data, size);
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF);
+ if (p != NULL) {
+ p->payload = (void*)data;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ if (to_in != NULL) {
+ inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr);
+ remote_port = ntohs(to_in->sin_port);
+ } else {
+ remote_addr = &sock->conn->pcb.raw->remote_ip;
+ if (sock->conn->type == NETCONN_RAW) {
+ remote_port = 0;
+ } else {
+ remote_port = sock->conn->pcb.udp->remote_port;
+ }
+ }
+
+ LOCK_TCPIP_CORE();
+ if (sock->conn->type == NETCONN_RAW) {
+ err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr);
+ } else {
+#if LWIP_UDP
+#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF
+ err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p,
+ remote_addr, remote_port, 1, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
+ err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p,
+ remote_addr, remote_port);
+#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
+#else /* LWIP_UDP */
+ err = ERR_ARG;
+#endif /* LWIP_UDP */
+ }
+ UNLOCK_TCPIP_CORE();
+
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ /* initialize a buffer */
+ buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ if (to) {
+ inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr);
+ remote_port = ntohs(to_in->sin_port);
+ netbuf_fromport(&buf) = remote_port;
+ } else {
+ remote_port = 0;
+ ip_addr_set_any(&buf.addr);
+ netbuf_fromport(&buf) = 0;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+ s, data, short_size, flags));
+ ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+ /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(&buf, short_size) == NULL) {
+ err = ERR_MEM;
+ } else {
+#if LWIP_CHECKSUM_ON_COPY
+ if (sock->conn->type != NETCONN_RAW) {
+ u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+ netbuf_set_chksum(&buf, chksum);
+ err = ERR_OK;
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ err = netbuf_take(&buf, data, short_size);
+ }
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (err == ERR_OK) {
+ /* send the data */
+ err = netconn_send(sock->conn, &buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_free(&buf);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+ struct netconn *conn;
+ int i;
+
+ LWIP_UNUSED_ARG(domain);
+
+ /* create a netconn */
+ switch (type) {
+ case SOCK_RAW:
+ conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_DGRAM:
+ conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ?
+ NETCONN_UDPLITE : NETCONN_UDP, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_STREAM:
+ conn = netconn_new_with_callback(NETCONN_TCP, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ if (conn != NULL) {
+ /* Prevent automatic window updates, we do this on our own! */
+ netconn_set_noautorecved(conn, 1);
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+ domain, type, protocol));
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ if (!conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+ set_errno(ENOBUFS);
+ return -1;
+ }
+
+ i = alloc_socket(conn, 0);
+
+ if (i == -1) {
+ netconn_delete(conn);
+ set_errno(ENFILE);
+ return -1;
+ }
+ conn->socket = i;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+ set_errno(0);
+ return i;
+}
+
+int
+lwip_write(int s, const void *data, size_t size)
+{
+ return lwip_send(s, data, size, 0);
+}
+
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * exceptset is not used for now!!!
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in: set of sockets to check for read events
+ * @param writeset_in: set of sockets to check for write events
+ * @param exceptset_in: set of sockets to check for error events
+ * @param readset_out: set of sockets that had read events
+ * @param writeset_out: set of sockets that had write events
+ * @param exceptset_out: set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+ fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+ int i, nready = 0;
+ fd_set lreadset, lwriteset, lexceptset;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ FD_ZERO(&lreadset);
+ FD_ZERO(&lwriteset);
+ FD_ZERO(&lexceptset);
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ for(i = 0; i < maxfdp1; i++) {
+ void* lastdata = NULL;
+ s16_t rcvevent = 0;
+ u16_t sendevent = 0;
+ u16_t errevent = 0;
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ lastdata = sock->lastdata;
+ rcvevent = sock->rcvevent;
+ sendevent = sock->sendevent;
+ errevent = sock->errevent;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+ FD_SET(i, &lreadset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket is ready for write */
+ if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+ FD_SET(i, &lwriteset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket had an error */
+ if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+ FD_SET(i, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+ nready++;
+ }
+ }
+ /* copy local sets to the ones provided as arguments */
+ *readset_out = lreadset;
+ *writeset_out = lwriteset;
+ *exceptset_out = lexceptset;
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+/**
+ * Processing exceptset is not yet implemented.
+ */
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ fd_set lreadset, lwriteset, lexceptset;
+ u32_t msectimeout;
+ struct lwip_select_cb select_cb;
+ err_t err;
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+ maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+ timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
+ timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (!nready) {
+ if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables. */
+
+ select_cb.next = NULL;
+ select_cb.prev = NULL;
+ select_cb.readset = readset;
+ select_cb.writeset = writeset;
+ select_cb.exceptset = exceptset;
+ select_cb.sem_signalled = 0;
+ err = sys_sem_new(&select_cb.sem, 0);
+ if (err != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ return -1;
+ }
+
+ /* Protect the select_cb_list */
+ SYS_ARCH_PROTECT(lev);
+
+ /* Put this select_cb on top of list */
+ select_cb.next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = &select_cb;
+ }
+ select_cb_list = &select_cb;
+ /* Increasing this counter tells even_callback that the list has changed. */
+ select_cb_ctr++;
+
+ /* Now we can safely unprotect */
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* Increase select_waiting for each socket we are interested in */
+ for(i = 0; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock = tryget_socket(i);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+ SYS_ARCH_PROTECT(lev);
+ sock->select_waiting++;
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+
+ /* Call lwip_selscan again: there could have been events between
+ the last scan (whithout us on the list) and putting us on the list! */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout == 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+ if (msectimeout == 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ }
+ }
+
+ waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
+ }
+ /* Increase select_waiting for each socket we are interested in */
+ for(i = 0; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock = tryget_socket(i);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+ SYS_ARCH_PROTECT(lev);
+ sock->select_waiting--;
+ LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ /* Take us off the list */
+ SYS_ARCH_PROTECT(lev);
+ if (select_cb.next != NULL) {
+ select_cb.next->prev = select_cb.prev;
+ }
+ if (select_cb_list == &select_cb) {
+ LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
+ select_cb_list = select_cb.next;
+ } else {
+ LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
+ select_cb.prev->next = select_cb.next;
+ }
+ /* Increasing this counter tells even_callback that the list has changed. */
+ select_cb_ctr++;
+ SYS_ARCH_UNPROTECT(lev);
+
+ sys_sem_free(&select_cb.sem);
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* See what's set */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_copy_fdsets:
+ set_errno(0);
+ if (readset) {
+ *readset = lreadset;
+ }
+ if (writeset) {
+ *writeset = lwriteset;
+ }
+ if (exceptset) {
+ *exceptset = lexceptset;
+ }
+
+
+ return nready;
+}
+
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+ int s;
+ struct lwip_sock *sock;
+ struct lwip_select_cb *scb;
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(len);
+
+ /* Get socket */
+ if (conn) {
+ s = conn->socket;
+ if (s < 0) {
+ /* Data comes in right away after an accept, even though
+ * the server task might not have created a new socket yet.
+ * Just count down (or up) if that's the case and we
+ * will use the data later. Note that only receive events
+ * can happen before the new socket is set up. */
+ SYS_ARCH_PROTECT(lev);
+ if (conn->socket < 0) {
+ if (evt == NETCONN_EVT_RCVPLUS) {
+ conn->socket--;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ s = conn->socket;
+ SYS_ARCH_UNPROTECT(lev);
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ SYS_ARCH_PROTECT(lev);
+ /* Set event as required */
+ switch (evt) {
+ case NETCONN_EVT_RCVPLUS:
+ sock->rcvevent++;
+ break;
+ case NETCONN_EVT_RCVMINUS:
+ sock->rcvevent--;
+ break;
+ case NETCONN_EVT_SENDPLUS:
+ sock->sendevent = 1;
+ break;
+ case NETCONN_EVT_SENDMINUS:
+ sock->sendevent = 0;
+ break;
+ case NETCONN_EVT_ERROR:
+ sock->errevent = 1;
+ break;
+ default:
+ LWIP_ASSERT("unknown event", 0);
+ break;
+ }
+
+ if (sock->select_waiting == 0) {
+ /* noone is waiting for this socket, no need to check select_cb_list */
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+
+ /* Now decide if anyone is waiting for this socket */
+ /* NOTE: This code goes through the select_cb_list list multiple times
+ ONLY IF a select was actually waiting. We go through the list the number
+ of waiting select calls + 1. This list is expected to be small. */
+
+ /* At this point, SYS_ARCH is still protected! */
+again:
+ for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+ if (scb->sem_signalled == 0) {
+ /* semaphore not signalled yet */
+ int do_signal = 0;
+ /* Test this select call for our socket */
+ if (sock->rcvevent > 0) {
+ if (scb->readset && FD_ISSET(s, scb->readset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->sendevent != 0) {
+ if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->errevent != 0) {
+ if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+ do_signal = 1;
+ }
+ }
+ if (do_signal) {
+ scb->sem_signalled = 1;
+ /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
+ lead to the select thread taking itself off the list, invalidagin the semaphore. */
+ sys_sem_signal(&scb->sem);
+ }
+ }
+ /* unlock interrupts with each step */
+ last_select_cb_ctr = select_cb_ctr;
+ SYS_ARCH_UNPROTECT(lev);
+ /* this makes sure interrupt protection time is short */
+ SYS_ARCH_PROTECT(lev);
+ if (last_select_cb_ctr != select_cb_ctr) {
+ /* someone has changed select_cb_list, restart at the beginning */
+ goto again;
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/**
+ * Unimplemented: Close one end of a full-duplex connection.
+ * Currently, the full connection is closed.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t shut_rx = 0, shut_tx = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ if (netconn_type(sock->conn) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
+ } else {
+ sock_set_errno(sock, ENOTCONN);
+ return ENOTCONN;
+ }
+
+ if (how == SHUT_RD) {
+ shut_rx = 1;
+ } else if (how == SHUT_WR) {
+ shut_tx = 1;
+ } else if(how == SHUT_RDWR) {
+ shut_rx = 1;
+ shut_tx = 1;
+ } else {
+ sock_set_errno(sock, EINVAL);
+ return EINVAL;
+ }
+ err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+ struct lwip_sock *sock;
+ struct sockaddr_in sin;
+ ip_addr_t naddr;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+
+ /* get the IP address and port */
+ netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port));
+
+ sin.sin_port = htons(sin.sin_port);
+ inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
+
+ if (*namelen > sizeof(sin)) {
+ *namelen = sizeof(sin);
+ }
+
+ MEMCPY(name, &sin, *namelen);
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ err_t err = ERR_OK;
+ struct lwip_sock *sock = get_socket(s);
+ struct lwip_setgetsockopt_data data;
+
+ if (!sock) {
+ return -1;
+ }
+
+ if ((NULL == optval) || (NULL == optlen)) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+ /* Do length and type checks for the various options first, to keep it readable. */
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ case SO_ACCEPTCONN:
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_ERROR:
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_CONTIMEO: */
+ /* UNIMPL case SO_SNDTIMEO: */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+#endif /* LWIP_SO_RCVBUF */
+ /* UNIMPL case SO_OOBINLINE: */
+ /* UNIMPL case SO_SNDBUF: */
+ /* UNIMPL case SO_RCVLOWAT: */
+ /* UNIMPL case SO_SNDLOWAT: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ case SO_TYPE:
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+
+ case SO_NO_CHECK:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+#if LWIP_UDP
+ if ((sock->conn->type != NETCONN_UDP) ||
+ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ err = EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDP */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ /* UNIMPL case IP_HDRINCL: */
+ /* UNIMPL case IP_RCVDSTADDR: */
+ /* UNIMPL case IP_RCVIF: */
+ case IP_TTL:
+ case IP_TOS:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ if (*optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (*optlen < sizeof(struct in_addr)) {
+ err = EINVAL;
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+#endif /* LWIP_IGMP */
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no TCP socket, ignore any options. */
+ if (sock->conn->type != NETCONN_TCP)
+ return 0;
+
+ switch (optname) {
+ case TCP_NODELAY:
+ case TCP_KEEPALIVE:
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ case TCP_KEEPINTVL:
+ case TCP_KEEPCNT:
+#endif /* LWIP_TCP_KEEPALIVE */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_UDP && LWIP_UDPLITE
+/* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no UDP lite socket, ignore any options. */
+ if (sock->conn->type != NETCONN_UDPLITE) {
+ return 0;
+ }
+
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ case UDPLITE_RECV_CSCOV:
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+/* UNDEFINED LEVEL */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ } /* switch */
+
+
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err);
+ return -1;
+ }
+
+ /* Now do the actual option processing */
+ data.sock = sock;
+#ifdef LWIP_DEBUG
+ data.s = s;
+#endif /* LWIP_DEBUG */
+ data.level = level;
+ data.optname = optname;
+ data.optval = optval;
+ data.optlen = optlen;
+ data.err = err;
+ tcpip_callback(lwip_getsockopt_internal, &data);
+ sys_arch_sem_wait(&sock->conn->op_completed, 0);
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = data.err;
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+static void
+lwip_getsockopt_internal(void *arg)
+{
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ int s;
+#endif /* LWIP_DEBUG */
+ int level, optname;
+ void *optval;
+ struct lwip_setgetsockopt_data *data;
+
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+
+ data = (struct lwip_setgetsockopt_data*)arg;
+ sock = data->sock;
+#ifdef LWIP_DEBUG
+ s = data->s;
+#endif /* LWIP_DEBUG */
+ level = data->level;
+ optname = data->optname;
+ optval = data->optval;
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* The option flags */
+ case SO_ACCEPTCONN:
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_OOBINCLUDE: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /*case SO_USELOOPBACK: UNIMPL */
+ *(int*)optval = sock->conn->pcb.ip->so_options & optname;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+
+ case SO_TYPE:
+ switch (NETCONNTYPE_GROUP(sock->conn->type)) {
+ case NETCONN_RAW:
+ *(int*)optval = SOCK_RAW;
+ break;
+ case NETCONN_TCP:
+ *(int*)optval = SOCK_STREAM;
+ break;
+ case NETCONN_UDP:
+ *(int*)optval = SOCK_DGRAM;
+ break;
+ default: /* unrecognized socket type */
+ *(int*)optval = sock->conn->type;
+ LWIP_DEBUGF(SOCKETS_DEBUG,
+ ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+ s, *(int *)optval));
+ } /* switch (sock->conn->type) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+ case SO_ERROR:
+ /* only overwrite ERR_OK or tempoary errors */
+ if ((sock->err == 0) || (sock->err == EINPROGRESS)) {
+ sock_set_errno(sock, err_to_errno(sock->conn->last_err));
+ }
+ *(int *)optval = sock->err;
+ sock->err = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ *(int *)optval = netconn_get_recvtimeout(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ *(int *)optval = netconn_get_recvbufsize(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+ break;
+#endif /* LWIP_UDP*/
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ *(int*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_TOS:
+ *(int*)optval = sock->conn->pcb.ip->tos;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+ s, *(int *)optval));
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ *(u8_t*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_MULTICAST_IF:
+ inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+ s, *(u32_t *)optval));
+ break;
+ case IP_MULTICAST_LOOP:
+ if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+ *(u8_t*)optval = 1;
+ } else {
+ *(u8_t*)optval = 0;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ switch (optname) {
+ case TCP_NODELAY:
+ *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+ s, (*(int*)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPINTVL:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPCNT:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled level", 0);
+ break;
+ } /* switch (level) */
+ sys_sem_signal(&sock->conn->op_completed);
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ struct lwip_sock *sock = get_socket(s);
+ err_t err = ERR_OK;
+ struct lwip_setgetsockopt_data data;
+
+ if (!sock) {
+ return -1;
+ }
+
+ if (NULL == optval) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+ /* Do length and type checks for the various options first, to keep it readable. */
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case case SO_CONTIMEO: */
+ /* UNIMPL case case SO_SNDTIMEO: */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+#endif /* LWIP_SO_RCVBUF */
+ /* UNIMPL case SO_OOBINLINE: */
+ /* UNIMPL case SO_SNDBUF: */
+ /* UNIMPL case SO_RCVLOWAT: */
+ /* UNIMPL case SO_SNDLOWAT: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+ case SO_NO_CHECK:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+#if LWIP_UDP
+ if ((sock->conn->type != NETCONN_UDP) ||
+ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ err = EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDP */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ /* UNIMPL case IP_HDRINCL: */
+ /* UNIMPL case IP_RCVDSTADDR: */
+ /* UNIMPL case IP_RCVIF: */
+ case IP_TTL:
+ case IP_TOS:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ if (optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (optlen < sizeof(struct in_addr)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ if (optlen < sizeof(struct ip_mreq)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no TCP socket, ignore any options. */
+ if (sock->conn->type != NETCONN_TCP)
+ return 0;
+
+ switch (optname) {
+ case TCP_NODELAY:
+ case TCP_KEEPALIVE:
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ case TCP_KEEPINTVL:
+ case TCP_KEEPCNT:
+#endif /* LWIP_TCP_KEEPALIVE */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_UDP && LWIP_UDPLITE
+/* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no UDP lite socket, ignore any options. */
+ if (sock->conn->type != NETCONN_UDPLITE)
+ return 0;
+
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ case UDPLITE_RECV_CSCOV:
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP && LWIP_UDPLITE */
+/* UNDEFINED LEVEL */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ } /* switch (level) */
+
+
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err);
+ return -1;
+ }
+
+
+ /* Now do the actual option processing */
+ data.sock = sock;
+#ifdef LWIP_DEBUG
+ data.s = s;
+#endif /* LWIP_DEBUG */
+ data.level = level;
+ data.optname = optname;
+ data.optval = (void*)optval;
+ data.optlen = &optlen;
+ data.err = err;
+ tcpip_callback(lwip_setsockopt_internal, &data);
+ sys_arch_sem_wait(&sock->conn->op_completed, 0);
+ /* maybe lwip_setsockopt_internal has changed err */
+ err = data.err;
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+static void
+lwip_setsockopt_internal(void *arg)
+{
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ int s;
+#endif /* LWIP_DEBUG */
+ int level, optname;
+ const void *optval;
+ struct lwip_setgetsockopt_data *data;
+
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+
+ data = (struct lwip_setgetsockopt_data*)arg;
+ sock = data->sock;
+#ifdef LWIP_DEBUG
+ s = data->s;
+#endif /* LWIP_DEBUG */
+ level = data->level;
+ optname = data->optname;
+ optval = data->optval;
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* The option flags */
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_OOBINCLUDE: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (*(int*)optval) {
+ sock->conn->pcb.ip->so_options |= optname;
+ } else {
+ sock->conn->pcb.ip->so_options &= ~optname;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ netconn_set_recvtimeout(sock->conn, *(int*)optval);
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ netconn_set_recvbufsize(sock->conn, *(int*)optval);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ if (*(int*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+ }
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+ s, sock->conn->pcb.ip->ttl));
+ break;
+ case IP_TOS:
+ sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+ s, sock->conn->pcb.ip->tos));
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval);
+ break;
+ case IP_MULTICAST_IF:
+ inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval);
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*(u8_t*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+ }
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ struct ip_mreq *imr = (struct ip_mreq *)optval;
+ ip_addr_t if_addr;
+ ip_addr_t multi_addr;
+ inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
+ inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
+ if(optname == IP_ADD_MEMBERSHIP){
+ data->err = igmp_joingroup(&if_addr, &multi_addr);
+ } else {
+ data->err = igmp_leavegroup(&if_addr, &multi_addr);
+ }
+ if(data->err != ERR_OK) {
+ data->err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ switch (optname) {
+ case TCP_NODELAY:
+ if (*(int*)optval) {
+ tcp_nagle_disable(sock->conn->pcb.tcp);
+ } else {
+ tcp_nagle_enable(sock->conn->pcb.tcp);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+ s, (*(int *)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+ case TCP_KEEPINTVL:
+ sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_intvl));
+ break;
+ case TCP_KEEPCNT:
+ sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_cnt));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP*/
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_tx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_rx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled level", 0);
+ break;
+ } /* switch (level) */
+ sys_sem_signal(&sock->conn->op_completed);
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+ struct lwip_sock *sock = get_socket(s);
+ u8_t val;
+#if LWIP_SO_RCVBUF
+ u16_t buflen = 0;
+ s16_t recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+#if LWIP_SO_RCVBUF
+ case FIONREAD:
+ if (!argp) {
+ sock_set_errno(sock, EINVAL);
+ return -1;
+ }
+
+ SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+ if (recv_avail < 0) {
+ recv_avail = 0;
+ }
+ *((u16_t*)argp) = (u16_t)recv_avail;
+
+ /* Check if there is data left from the last recv operation. /maq 041215 */
+ if (sock->lastdata) {
+ struct pbuf *p = (struct pbuf *)sock->lastdata;
+ if (netconn_type(sock->conn) != NETCONN_TCP) {
+ p = ((struct netbuf *)p)->p;
+ }
+ buflen = p->tot_len;
+ buflen -= sock->lastoffset;
+
+ *((u16_t*)argp) += buflen;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
+ sock_set_errno(sock, 0);
+ return 0;
+#endif /* LWIP_SO_RCVBUF */
+
+ case FIONBIO:
+ val = 0;
+ if (argp && *(u32_t*)argp) {
+ val = 1;
+ }
+ netconn_set_nonblocking(sock->conn, val);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+ sock_set_errno(sock, 0);
+ return 0;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ return -1;
+ } /* switch (cmd) */
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * Only the flag O_NONBLOCK is implemented.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int ret = -1;
+
+ if (!sock || !sock->conn) {
+ return -1;
+ }
+
+ switch (cmd) {
+ case F_GETFL:
+ ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ break;
+ case F_SETFL:
+ if ((val & ~O_NONBLOCK) == 0) {
+ /* only O_NONBLOCK, all other bits are zero */
+ netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+ ret = 0;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+ break;
+ }
+ return ret;
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/core/lwip/src/api/tcpip.c b/core/lwip/src/api/tcpip.c
new file mode 100644
index 00000000..857e7d9b
--- /dev/null
+++ b/core/lwip/src/api/tcpip.c
@@ -0,0 +1,460 @@
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcpip.h"
+#include "lwip/init.h"
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+ struct tcpip_msg *msg;
+ LWIP_UNUSED_ARG(arg);
+
+ if (tcpip_init_done != NULL) {
+ tcpip_init_done(tcpip_init_done_arg);
+ }
+
+ LOCK_TCPIP_CORE();
+ while (1) { /* MAIN Loop */
+ UNLOCK_TCPIP_CORE();
+ LWIP_TCPIP_THREAD_ALIVE();
+ /* wait for a message, timeouts are processed while waiting */
+ sys_timeouts_mbox_fetch(&mbox, (void **)&msg);
+ LOCK_TCPIP_CORE();
+ switch (msg->type) {
+#if LWIP_NETCONN
+ case TCPIP_MSG_API:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+ msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
+ break;
+#endif /* LWIP_NETCONN */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+ case TCPIP_MSG_INPKT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+#if LWIP_ETHERNET
+ if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
+ } else
+#endif /* LWIP_ETHERNET */
+ {
+ ip_input(msg->msg.inp.p, msg->msg.inp.netif);
+ }
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ break;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_NETIF_API
+ case TCPIP_MSG_NETIFAPI:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
+ msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
+ break;
+#endif /* LWIP_NETIF_API */
+
+ case TCPIP_MSG_CALLBACK:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+
+#if LWIP_TCPIP_TIMEOUT
+ case TCPIP_MSG_TIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+ sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+ case TCPIP_MSG_UNTIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+ sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+ default:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ break;
+ }
+ }
+}
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ * NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+ err_t ret;
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp));
+ LOCK_TCPIP_CORE();
+#if LWIP_ETHERNET
+ if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ ret = ethernet_input(p, inp);
+ } else
+#endif /* LWIP_ETHERNET */
+ {
+ ret = ip_input(p, inp);
+ }
+ UNLOCK_TCPIP_CORE();
+ return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_INPKT;
+ msg->msg.inp.p = p;
+ msg->msg.inp.netif = inp;
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ return ERR_MEM;
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ *
+ * @param f the function to call
+ * @param ctx parameter passed to f
+ * @param block 1 to block until the request is posted, 0 to non-blocking mode
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+ if (block) {
+ sys_mbox_post(&mbox, msg);
+ } else {
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ return ERR_MEM;
+ }
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_TCPIP_TIMEOUT
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msec time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_TIMEOUT;
+ msg->msg.tmo.msecs = msecs;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param msec time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_UNTIMEOUT;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+#if LWIP_NETCONN
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_apimsg(struct api_msg *apimsg)
+{
+ struct tcpip_msg msg;
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->msg.err = ERR_VAL;
+#endif
+
+ if (sys_mbox_valid(&mbox)) {
+ msg.type = TCPIP_MSG_API;
+ msg.msg.apimsg = apimsg;
+ sys_mbox_post(&mbox, &msg);
+ sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);
+ return apimsg->msg.err;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_TCPIP_CORE_LOCKING
+/**
+ * Call the lower part of a netconn_* function
+ * This function has exclusive access to lwIP core code by locking it
+ * before the function is called.
+ *
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK (only for compatibility fo tcpip_apimsg())
+ */
+err_t
+tcpip_apimsg_lock(struct api_msg *apimsg)
+{
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->msg.err = ERR_VAL;
+#endif
+
+ LOCK_TCPIP_CORE();
+ apimsg->function(&(apimsg->msg));
+ UNLOCK_TCPIP_CORE();
+ return apimsg->msg.err;
+
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETCONN */
+
+#if LWIP_NETIF_API
+#if !LWIP_TCPIP_CORE_LOCKING
+/**
+ * Much like tcpip_apimsg, but calls the lower part of a netifapi_*
+ * function.
+ *
+ * @param netifapimsg a struct containing the function to call and its parameters
+ * @return error code given back by the function that was called
+ */
+err_t
+tcpip_netifapi(struct netifapi_msg* netifapimsg)
+{
+ struct tcpip_msg msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
+ if (err != ERR_OK) {
+ netifapimsg->msg.err = err;
+ return err;
+ }
+
+ msg.type = TCPIP_MSG_NETIFAPI;
+ msg.msg.netifapimsg = netifapimsg;
+ sys_mbox_post(&mbox, &msg);
+ sys_sem_wait(&netifapimsg->msg.sem);
+ sys_sem_free(&netifapimsg->msg.sem);
+ return netifapimsg->msg.err;
+ }
+ return ERR_VAL;
+}
+#else /* !LWIP_TCPIP_CORE_LOCKING */
+/**
+ * Call the lower part of a netifapi_* function
+ * This function has exclusive access to lwIP core code by locking it
+ * before the function is called.
+ *
+ * @param netifapimsg a struct containing the function to call and its parameters
+ * @return ERR_OK (only for compatibility fo tcpip_netifapi())
+ */
+err_t
+tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
+{
+ LOCK_TCPIP_CORE();
+ netifapimsg->function(&(netifapimsg->msg));
+ UNLOCK_TCPIP_CORE();
+ return netifapimsg->msg.err;
+}
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETIF_API */
+
+/**
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+ lwip_init();
+
+ tcpip_init_done = initfunc;
+ tcpip_init_done_arg = arg;
+ if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+ LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+ LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+ }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+ struct pbuf *q = (struct pbuf *)p;
+ pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+ return tcpip_callback_with_block(pbuf_free_int, p, 0);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+ return tcpip_callback_with_block(mem_free, m, 0);
+}
+
+#endif /* !NO_SYS */
diff --git a/core/lwip/src/arch/sys_arch.c b/core/lwip/src/arch/sys_arch.c
new file mode 100644
index 00000000..4081d01e
--- /dev/null
+++ b/core/lwip/src/arch/sys_arch.c
@@ -0,0 +1,131 @@
+#include "arch/sys_arch.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include <stdlib.h>
+#include <thread.h>
+
+void sys_init(void)
+{
+}
+
+err_t sys_sem_new(sys_sem_t *sem, u8_t count)
+{
+ if (!sem)
+ return EINVAL;
+ *sem = malloc(sizeof(struct semaphore));
+ if (!*sem)
+ return ENOMEM;
+
+ sem_init(*sem, count);
+ return 0;
+}
+
+void sys_sem_free(sys_sem_t *sem)
+{
+ if (!!sem && !!*sem) {
+ sys_sem_set_invalid(sem);
+ free(*sem);
+ *sem = NULL;
+ }
+}
+
+void sys_sem_set_invalid(sys_sem_t *sem)
+{
+ if (!sem || !*sem)
+ return;
+ sem_set_invalid(*sem);
+}
+
+
+int sys_sem_valid(sys_sem_t *sem)
+{
+ if (!sem || !*sem)
+ return 0;
+ return sem_is_valid(*sem);
+}
+
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+{
+ mstime_t rv;
+
+ if (!sem || !*sem)
+ return SYS_ARCH_TIMEOUT;
+ rv = sem_down(*sem, timeout);
+ if (rv == (mstime_t)-1)
+ return SYS_ARCH_TIMEOUT;
+ else
+ return rv;
+}
+
+err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+{
+ if (!mbox)
+ return EINVAL;
+ *mbox = malloc(MBOX_BYTES(size));
+ if (!(*mbox))
+ return ENOMEM;
+
+ mbox_init(*mbox, size);
+ return 0;
+}
+
+void sys_mbox_free(sys_mbox_t *mbox)
+{
+ if (!!mbox && !!*mbox) {
+ sys_mbox_set_invalid(mbox);
+ free(*mbox);
+ *mbox = NULL;
+ }
+}
+
+void sys_mbox_post(sys_mbox_t *mbox, void *msg)
+{
+ if (!!mbox)
+ mbox_post(*mbox, msg, 0);
+}
+
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
+{
+ if (!mbox)
+ return EINVAL;
+ return mbox_post(*mbox, msg, -1);
+}
+
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
+{
+ mstime_t rv;
+
+ if (!mbox)
+ return SYS_MBOX_EMPTY;
+ rv = mbox_fetch(*mbox, msg, timeout);
+ if (rv == (mstime_t)-1)
+ return SYS_ARCH_TIMEOUT;
+ else
+ return rv;
+}
+
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
+{
+ if (!mbox)
+ return SYS_MBOX_EMPTY;
+ return mbox_fetch(*mbox, msg, -1);
+}
+
+void sys_mbox_set_invalid(sys_mbox_t *mbox)
+{
+ if (!!mbox)
+ mbox_set_invalid(*mbox);
+}
+
+int sys_mbox_valid(sys_mbox_t *mbox)
+{
+ return ((!!mbox) && mbox_is_valid(*mbox));
+}
+
+
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread,
+ void *arg, int stacksize, int prio)
+{
+ return start_thread(name, stacksize, prio, thread, arg);
+}
+
diff --git a/core/lwip/src/core/def.c b/core/lwip/src/core/def.c
new file mode 100644
index 00000000..352b5524
--- /dev/null
+++ b/core/lwip/src/core/def.c
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+/**
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * #define LWIP_PLATFORM_BYTESWAP 1
+ * #define LWIP_PLATFORM_HTONS(x) <your_htons>
+ * #define LWIP_PLATFORM_HTONL(x) <your_htonl>
+ *
+ * Note ntohs() and ntohl() are merely references to the htonx counterparts.
+ */
+
+#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)
+
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+ return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
+}
+
+/**
+ * Convert an u16_t from network- to host byte order.
+ *
+ * @param n u16_t in network byte order
+ * @return n in host byte order
+ */
+u16_t
+lwip_ntohs(u16_t n)
+{
+ return lwip_htons(n);
+}
+
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+ return ((n & 0xff) << 24) |
+ ((n & 0xff00) << 8) |
+ ((n & 0xff0000UL) >> 8) |
+ ((n & 0xff000000UL) >> 24);
+}
+
+/**
+ * Convert an u32_t from network- to host byte order.
+ *
+ * @param n u32_t in network byte order
+ * @return n in host byte order
+ */
+u32_t
+lwip_ntohl(u32_t n)
+{
+ return lwip_htonl(n);
+}
+
+#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */
diff --git a/core/lwip/src/core/dhcp.c b/core/lwip/src/core/dhcp.c
new file mode 100644
index 00000000..81b4be27
--- /dev/null
+++ b/core/lwip/src/core/dhcp.c
@@ -0,0 +1,1745 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * TODO:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Please coordinate changes and requests with Leon Woestenberg
+ * <leon.woestenberg@gmx.net>
+ *
+ * Integration with your code:
+ *
+ * In lwip/dhcp.h
+ * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * Then have your application call dhcp_coarse_tmr() and
+ * dhcp_fine_tmr() on the defined intervals.
+ *
+ * dhcp_start(struct netif *netif);
+ * starts a DHCP client instance which configures the interface by
+ * obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif)
+ * to remove the DHCP client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "netif/etharp.h"
+
+#include <string.h>
+
+/** Default for DHCP_GLOBAL_XID is 0xABCD0000
+ * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
+ * #define DHCP_GLOBAL_XID_HEADER "stdlib.h"
+ * #define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif) (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN 44
+
+#define REBOOT_TRIES 2
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+#define DHCP_OPTION_IDX_OVERLOAD 0
+#define DHCP_OPTION_IDX_MSG_TYPE 1
+#define DHCP_OPTION_IDX_SERVER_ID 2
+#define DHCP_OPTION_IDX_LEASE_TIME 3
+#define DHCP_OPTION_IDX_T1 4
+#define DHCP_OPTION_IDX_T2 5
+#define DHCP_OPTION_IDX_SUBNET_MASK 6
+#define DHCP_OPTION_IDX_ROUTER 7
+#define DHCP_OPTION_IDX_DNS_SERVER 8
+#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+ only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
+/* free a DHCP request */
+static void dhcp_delete_msg(struct dhcp *dhcp);
+/* add a DHCP option (type, then length in bytes) */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
+/* add option values */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Set the interface down since the address must no longer be used, as per RFC2131 */
+ netif_set_down(netif);
+ /* remove IP address from interface */
+ netif_set_ipaddr(netif, IP_ADDR_ANY);
+ netif_set_gw(netif, IP_ADDR_ANY);
+ netif_set_netmask(netif, IP_ADDR_ANY);
+ /* Change to a defined state */
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* We can immediately restart discovery */
+ dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+ (s16_t)netif->name[1]));
+ dhcp_set_state(dhcp, DHCP_CHECKING);
+ /* create an ARP query for the offered IP address, expecting that no host
+ responds, as the IP address should not be in use. */
+ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+ if (result != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+ }
+ dhcp->tries++;
+ msecs = 500;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* obtain the server address */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+ ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->server_ip_addr)));
+ /* remember offered address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
+ }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ dhcp_set_state(dhcp, DHCP_REQUESTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+ dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+ dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+ dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+ dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* send broadcast to any DHCP server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ */
+void
+dhcp_coarse_tmr()
+{
+ struct netif *netif = netif_list;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and triggers (zeroes) now? */
+ if (netif->dhcp->t2_timeout-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+ /* this clients' rebind timeout triggered */
+ dhcp_t2_timeout(netif);
+ /* timer is active (non zero), and triggers (zeroes) now */
+ } else if (netif->dhcp->t1_timeout-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+ /* this clients' renewal timeout triggered */
+ dhcp_t1_timeout(netif);
+ }
+ }
+ /* proceed to next netif */
+ netif = netif->next;
+ }
+}
+
+/**
+ * DHCP transaction timeout handling
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (netif->dhcp->request_timeout > 1) {
+ netif->dhcp->request_timeout--;
+ }
+ else if (netif->dhcp->request_timeout == 1) {
+ netif->dhcp->request_timeout--;
+ /* { netif->dhcp->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp_timeout(netif);
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+ /* back-off period has passed, or server selection timed out */
+ if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+ dhcp_discover(netif);
+ /* receiving the requested lease timed out */
+ } else if (dhcp->state == DHCP_REQUESTING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+ if (dhcp->tries <= 5) {
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+#if DHCP_DOES_ARP_CHECK
+ /* received no ARP reply for the offered address (which is good) */
+ } else if (dhcp->state == DHCP_CHECKING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+ if (dhcp->tries <= 1) {
+ dhcp_check(netif);
+ /* no ARP replies on the offered address,
+ looks like the IP address is indeed free */
+ } else {
+ /* bind the interface to the offered address */
+ dhcp_bind(netif);
+ }
+#endif /* DHCP_DOES_ARP_CHECK */
+ }
+ /* did not get response to renew request? */
+ else if (dhcp->state == DHCP_RENEWING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n"));
+ /* just retry renewal */
+ /* note that the rebind timer will eventually time-out if renew does not work */
+ dhcp_renew(netif);
+ /* did not get response to rebind request? */
+ } else if (dhcp->state == DHCP_REBINDING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n"));
+ if (dhcp->tries <= 8) {
+ dhcp_rebind(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+ } else if (dhcp->state == DHCP_REBOOTING) {
+ if (dhcp->tries < REBOOT_TRIES) {
+ dhcp_reboot(netif);
+ } else {
+ dhcp_discover(netif);
+ }
+ }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to renew - note that the rebind timer (t2) will
+ * eventually time-out if renew tries fail. */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t1_timeout(): must renew\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_RENEWING, not DHCP_BOUND */
+ dhcp_renew(netif);
+ }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to rebind */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t2_timeout(): must rebind\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_REBINDING, not DHCP_BOUND */
+ dhcp_rebind(netif);
+ }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+#if LWIP_DNS
+ u8_t n;
+#endif /* LWIP_DNS */
+
+ /* clear options we might not get from the ACK */
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* lease time given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+ /* remember offered lease time */
+ dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+ }
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+ /* remember given renewal period */
+ dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+ } else {
+ /* calculate safe periods for renewal */
+ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+ }
+
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+ /* remember given rebind period */
+ dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+ } else {
+ /* calculate safe periods for rebinding */
+ dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
+ }
+
+ /* (y)our internet address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+ /* copy boot server address,
+ boot file name copied in dhcp_parse_reply if not overloaded */
+ ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* subnet mask given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+ /* remember given subnet mask */
+ ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+ dhcp->subnet_mask_given = 1;
+ } else {
+ dhcp->subnet_mask_given = 0;
+ }
+
+ /* gateway router */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+ ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+ }
+
+#if LWIP_DNS
+ /* DNS servers */
+ n = 0;
+ while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) {
+ ip_addr_t dns_addr;
+ ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ dns_setserver(n, &dns_addr);
+ n++;
+ }
+#endif /* LWIP_DNS */
+}
+
+/** Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL);
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ netif->dhcp = dhcp;
+}
+
+/** Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ * struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif->dhcp != NULL) {
+ mem_free(netif->dhcp);
+ netif->dhcp = NULL;
+ }
+}
+
+/**
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result = ERR_OK;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+ dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Remove the flag that says this netif is handled by DHCP,
+ it is set when we succeeded starting. */
+ netif->flags &= ~NETIF_FLAG_DHCP;
+
+ /* check hwtype of the netif */
+ if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
+ return ERR_ARG;
+ }
+
+ /* check MTU of the netif */
+ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+ return ERR_MEM;
+ }
+
+ /* no DHCP client attached yet? */
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+ dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+ return ERR_MEM;
+ }
+ /* store this dhcp client in the netif */
+ netif->dhcp = dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+ /* already has DHCP client attached */
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ }
+ LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
+ }
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ /* allocate UDP PCB */
+ dhcp->pcb = udp_new();
+ if (dhcp->pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
+ return ERR_MEM;
+ }
+ dhcp->pcb->so_options |= SOF_BROADCAST;
+ /* set up local and remote port for the pcb */
+ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+ /* set up the recv callback and argument */
+ udp_recv(dhcp->pcb, dhcp_recv, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+ /* (re)start the DHCP negotiation */
+ result = dhcp_discover(netif);
+ if (result != ERR_OK) {
+ /* free resources allocated above */
+ dhcp_stop(netif);
+ return ERR_MEM;
+ }
+ /* Set the flag that says this netif is handled by DHCP. */
+ netif->flags |= NETIF_FLAG_DHCP;
+ return result;
+}
+
+/**
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+ struct dhcp dhcp;
+ err_t result = ERR_OK;
+ struct udp_pcb *pcb;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ memset(&dhcp, 0, sizeof(struct dhcp));
+ dhcp_set_state(&dhcp, DHCP_INFORM);
+
+ if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) {
+ /* re-use existing pcb */
+ pcb = netif->dhcp->pcb;
+ } else {
+ pcb = udp_new();
+ if (pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb"));
+ return;
+ }
+ dhcp.pcb = pcb;
+ dhcp.pcb->so_options |= SOF_BROADCAST;
+ udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n"));
+ }
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
+ if (result == ERR_OK) {
+ dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option_trailer(&dhcp);
+
+ pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+ udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(&dhcp);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+ }
+
+ if (dhcp.pcb != NULL) {
+ /* otherwise, the existing pcb was used */
+ udp_remove(dhcp.pcb);
+ }
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ if (!dhcp)
+ return;
+ switch (dhcp->state) {
+ case DHCP_REBINDING:
+ case DHCP_RENEWING:
+ case DHCP_BOUND:
+ case DHCP_REBOOTING:
+ netif_set_down(netif);
+ dhcp->tries = 0;
+ dhcp_reboot(netif);
+ break;
+ case DHCP_OFF:
+ /* stay off */
+ break;
+ default:
+ dhcp->tries = 0;
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ dhcp_discover(netif);
+ break;
+ }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address.
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr)
+{
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+ /* is a DHCP client doing an ARP check? */
+ if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(addr)));
+ /* did a host respond with the address we
+ were offered by the DHCP server? */
+ if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) {
+ /* we will not accept the offered address */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+ dhcp_decline(netif);
+ }
+ }
+}
+
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+ /* resize pbuf to reflect true size of options */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_decline: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = 10*1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+ ip_addr_set_any(&dhcp->offered_ip_addr);
+ dhcp_set_state(dhcp, DHCP_SELECTING);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
+ if (result == ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+ dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+ dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+ dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+ dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+ dhcp_option_trailer(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+ autoip_start(netif);
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+ u32_t timeout;
+ struct dhcp *dhcp;
+ ip_addr_t sn_mask, gw_addr;
+ LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* temporary DHCP lease? */
+ if (dhcp->offered_t1_renew != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+ timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t1_timeout = (u16_t)timeout;
+ if (dhcp->t1_timeout == 0) {
+ dhcp->t1_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+ }
+ /* set renewal period timer */
+ if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+ timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t2_timeout = (u16_t)timeout;
+ if (dhcp->t2_timeout == 0) {
+ dhcp->t2_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+ }
+
+ if (dhcp->subnet_mask_given) {
+ /* copy offered network mask */
+ ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
+ } else {
+ /* subnet mask not given, choose a safe subnet mask given the network class */
+ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+ if (first_octet <= 127) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
+ } else if (first_octet >= 192) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
+ } else {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
+ }
+ }
+
+ ip_addr_copy(gw_addr, dhcp->offered_gw_addr);
+ /* gateway address not given? */
+ if (ip_addr_isany(&gw_addr)) {
+ /* copy network address */
+ ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+ /* use first host address on network as gateway */
+ ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
+ }
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+ netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&sn_mask)));
+ netif_set_netmask(netif, &sn_mask);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&gw_addr)));
+ netif_set_gw(netif, &gw_addr);
+ /* bring the interface up */
+ netif_set_up(netif);
+ /* netif is now bound to DHCP leased address */
+ dhcp_set_state(dhcp, DHCP_BOUND);
+}
+
+/**
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+#endif
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+ /* append DHCP message trailer */
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+ dhcp_set_state(dhcp, DHCP_REBINDING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+ dhcp_set_state(dhcp, DHCP_REBOOTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, 576);
+
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Release a DHCP lease.
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+
+ /* idle DHCP client */
+ dhcp_set_state(dhcp, DHCP_OFF);
+ /* clean old DHCP offer */
+ ip_addr_set_zero(&dhcp->server_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+ if (result == ERR_OK) {
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs));
+ /* bring the interface down */
+ netif_set_down(netif);
+ /* remove IP address from interface */
+ netif_set_ipaddr(netif, IP_ADDR_ANY);
+ netif_set_gw(netif, IP_ADDR_ANY);
+ netif_set_netmask(netif, IP_ADDR_ANY);
+
+ return result;
+}
+
+/**
+ * Remove the DHCP client from the interface.
+ *
+ * @param netif The network interface to stop DHCP on
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ /* Remove the flag that says this netif is handled by DHCP. */
+ netif->flags &= ~NETIF_FLAG_DHCP;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
+ /* netif is DHCP configured? */
+ if (dhcp != NULL) {
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ dhcp->pcb = NULL;
+ }
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+ dhcp_set_state(dhcp, DHCP_OFF);
+ }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+ if (new_state != dhcp->state) {
+ dhcp->state = new_state;
+ dhcp->tries = 0;
+ dhcp->request_timeout = 0;
+ }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void
+dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static void
+dhcp_option_byte(struct dhcp *dhcp, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = value;
+}
+
+static void
+dhcp_option_short(struct dhcp *dhcp, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU);
+}
+
+static void
+dhcp_option_long(struct dhcp *dhcp, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
+}
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a conitguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
+{
+ u8_t *options;
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ u16_t options_idx_max;
+ struct pbuf *q;
+ int parse_file_as_options = 0;
+ int parse_sname_as_options = 0;
+
+ /* clear received options */
+ dhcp_clear_all_options(dhcp);
+ /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+ if (p->len < DHCP_SNAME_OFS) {
+ return ERR_BUF;
+ }
+ dhcp->msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+ /* clear boot file name */
+ dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* parse options */
+
+ /* start with options field */
+ options_idx = DHCP_OPTIONS_OFS;
+ /* parse options to the end of the received packet */
+ options_idx_max = p->tot_len;
+again:
+ q = p;
+ while((q != NULL) && (options_idx >= q->len)) {
+ options_idx -= q->len;
+ options_idx_max -= q->len;
+ q = q->next;
+ }
+ if (q == NULL) {
+ return ERR_BUF;
+ }
+ offset = options_idx;
+ offset_max = options_idx_max;
+ options = (u8_t*)q->payload;
+ /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+ while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = offset + 2;
+ /* len byte might be in the next pbuf */
+ if (offset + 1 < q->len) {
+ len = options[offset + 1];
+ } else {
+ len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
+ }
+ /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+ decode_len = len;
+ switch(op) {
+ /* case(DHCP_OPTION_END): handled above */
+ case(DHCP_OPTION_PAD):
+ /* special option: no len encoded */
+ decode_len = len = 0;
+ /* will be increased below */
+ offset--;
+ break;
+ case(DHCP_OPTION_SUBNET_MASK):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+ break;
+ case(DHCP_OPTION_ROUTER):
+ decode_len = 4; /* only copy the first given router */
+ LWIP_ASSERT("len >= decode_len", len >= decode_len);
+ decode_idx = DHCP_OPTION_IDX_ROUTER;
+ break;
+ case(DHCP_OPTION_DNS_SERVER):
+ /* special case: there might be more than one server */
+ LWIP_ASSERT("len % 4 == 0", len % 4 == 0);
+ /* limit number of DNS servers */
+ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+ LWIP_ASSERT("len >= decode_len", len >= decode_len);
+ decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+ break;
+ case(DHCP_OPTION_LEASE_TIME):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+ break;
+ case(DHCP_OPTION_OVERLOAD):
+ LWIP_ASSERT("len == 1", len == 1);
+ decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+ break;
+ case(DHCP_OPTION_MESSAGE_TYPE):
+ LWIP_ASSERT("len == 1", len == 1);
+ decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+ break;
+ case(DHCP_OPTION_SERVER_ID):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+ break;
+ case(DHCP_OPTION_T1):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_T1;
+ break;
+ case(DHCP_OPTION_T2):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_T2;
+ break;
+ default:
+ decode_len = 0;
+ LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
+ break;
+ }
+ offset += len + 2;
+ if (decode_len > 0) {
+ u32_t value = 0;
+ u16_t copy_len;
+decode_next:
+ LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+ LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx));
+ copy_len = LWIP_MIN(decode_len, 4);
+ pbuf_copy_partial(q, &value, copy_len, val_offset);
+ if (decode_len > 4) {
+ /* decode more than one u32_t */
+ LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0);
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, htonl(value));
+ decode_len -= 4;
+ val_offset += 4;
+ decode_idx++;
+ goto decode_next;
+ } else if (decode_len == 4) {
+ value = ntohl(value);
+ } else {
+ LWIP_ASSERT("invalid decode_len", decode_len == 1);
+ value = ((u8_t*)&value)[0];
+ }
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, value);
+ }
+ if (offset >= q->len) {
+ offset -= q->len;
+ offset_max -= q->len;
+ q = q->next;
+ options = (u8_t*)q->payload;
+ }
+ }
+ /* is this an overloaded message? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+ u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ if (overload == DHCP_OVERLOAD_FILE) {
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME) {
+ parse_sname_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+ parse_sname_as_options = 1;
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+ }
+#if LWIP_DHCP_BOOTP_FILE
+ if (!parse_file_as_options) {
+ /* only do this for ACK messages */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+ (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+ /* copy bootp file name, don't care for sname (server hostname) */
+ pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS);
+ /* make sure the string is really NULL-terminated */
+ dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+ }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ }
+ if (parse_file_as_options) {
+ /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+ parse_file_as_options = 0;
+ options_idx = DHCP_FILE_OFS;
+ options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+ goto again;
+ } else if (parse_sname_as_options) {
+ parse_sname_as_options = 0;
+ options_idx = DHCP_SNAME_OFS;
+ options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+ goto again;
+ }
+ return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = (struct netif *)arg;
+ struct dhcp *dhcp = netif->dhcp;
+ struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+ u8_t msg_type;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+ ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+
+ if (p->len < DHCP_MIN_REPLY_LEN) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ if (reply_msg->op != DHCP_BOOTREPLY) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+ goto free_pbuf_and_return;
+ }
+ /* iterate through hardware address and match against DHCP message */
+ for (i = 0; i < netif->hwaddr_len; i++) {
+ if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+ (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+ goto free_pbuf_and_return;
+ }
+ }
+ /* match transaction ID against what we expected */
+ if (ntohl(reply_msg->xid) != dhcp->xid) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCP message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+ /* obtain pointer to DHCP message type */
+ if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* read DHCP message type */
+ msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+ /* message type is DHCP ACK? */
+ if (msg_type == DHCP_ACK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+ /* in requesting state? */
+ if (dhcp->state == DHCP_REQUESTING) {
+ dhcp_handle_ack(netif);
+#if DHCP_DOES_ARP_CHECK
+ /* check if the acknowledged lease address is already in use */
+ dhcp_check(netif);
+#else
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+#endif
+ }
+ /* already bound to the given lease address? */
+ else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
+ dhcp_bind(netif);
+ }
+ }
+ /* received a DHCP_NAK in appropriate state? */
+ else if ((msg_type == DHCP_NAK) &&
+ ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
+ (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+ dhcp_handle_nak(netif);
+ }
+ /* received a DHCP_OFFER in DHCP_SELECTING state? */
+ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
+ dhcp->request_timeout = 0;
+ /* remember offered lease */
+ dhcp_handle_offer(netif);
+ }
+free_pbuf_and_return:
+ dhcp->msg_in = NULL;
+ pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static err_t
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
+{
+ u16_t i;
+#ifndef DHCP_GLOBAL_XID
+ /** default global transaction identifier starting value (easy to match
+ * with a packet analyser). We simply increment for each new request.
+ * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+ * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+ static u32_t xid = 0xABCD0000;
+#else
+ static u32_t xid;
+ static u8_t xid_initialised = 0;
+ if (!xid_initialised) {
+ xid = DHCP_GLOBAL_XID;
+ xid_initialised = !xid_initialised;
+ }
+#endif
+ LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
+ dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (dhcp->p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp->tries == 0) {
+ xid++;
+ }
+ dhcp->xid = xid;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", xid));
+
+ dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
+
+ dhcp->msg_out->op = DHCP_BOOTREQUEST;
+ /* TODO: make link layer independent */
+ dhcp->msg_out->htype = DHCP_HTYPE_ETH;
+ dhcp->msg_out->hlen = netif->hwaddr_len;
+ dhcp->msg_out->hops = 0;
+ dhcp->msg_out->xid = htonl(dhcp->xid);
+ dhcp->msg_out->secs = 0;
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ dhcp->msg_out->flags = 0;
+ ip_addr_set_zero(&dhcp->msg_out->ciaddr);
+ /* set ciaddr to netif->ip_addr based on message_type and state */
+ if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) ||
+ ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
+ ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
+ ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
+ }
+ ip_addr_set_zero(&dhcp->msg_out->yiaddr);
+ ip_addr_set_zero(&dhcp->msg_out->siaddr);
+ ip_addr_set_zero(&dhcp->msg_out->giaddr);
+ for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+ /* copy netif hardware address, pad with zeroes */
+ dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/;
+ }
+ for (i = 0; i < DHCP_SNAME_LEN; i++) {
+ dhcp->msg_out->sname[i] = 0;
+ }
+ for (i = 0; i < DHCP_FILE_LEN; i++) {
+ dhcp->msg_out->file[i] = 0;
+ }
+ dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ dhcp->options_out_len = 0;
+ /* fill options field with an incrementing array (for debugging purposes) */
+ for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
+ dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
+ }
+ /* Add option MESSAGE_TYPE */
+ dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ dhcp_option_byte(dhcp, message_type);
+ return ERR_OK;
+}
+
+/**
+ * Free previously allocated memory used to send a DHCP request.
+ *
+ * @param dhcp the dhcp struct to free the request from
+ */
+static void
+dhcp_delete_msg(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+ if (dhcp->p_out != NULL) {
+ pbuf_free(dhcp->p_out);
+ }
+ dhcp->p_out = NULL;
+ dhcp->msg_out = NULL;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ *
+ * @param dhcp DHCP state structure
+ */
+static void
+dhcp_option_trailer(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) {
+ /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ /* add a fill/padding byte */
+ dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+ }
+}
+
+#endif /* LWIP_DHCP */
diff --git a/core/lwip/src/core/dns.c b/core/lwip/src/core/dns.c
new file mode 100644
index 00000000..ca807c14
--- /dev/null
+++ b/core/lwip/src/core/dns.c
@@ -0,0 +1,975 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ */
+
+/**
+
+ * This file implements a DNS host name to IP address resolver.
+
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * DNS.C
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+/** DNS server IP address */
+#ifndef DNS_SERVER_ADDRESS
+#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT 53
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES 4
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL 604800
+#endif
+
+/* DNS protocol flags */
+#define DNS_FLAG1_RESPONSE 0x80
+#define DNS_FLAG1_OPCODE_STATUS 0x10
+#define DNS_FLAG1_OPCODE_INVERSE 0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE 0x04
+#define DNS_FLAG1_TRUNC 0x02
+#define DNS_FLAG1_RD 0x01
+#define DNS_FLAG2_RA 0x80
+#define DNS_FLAG2_ERR_MASK 0x0f
+#define DNS_FLAG2_ERR_NONE 0x00
+#define DNS_FLAG2_ERR_NAME 0x03
+
+/* DNS protocol states */
+#define DNS_STATE_UNUSED 0
+#define DNS_STATE_NEW 1
+#define DNS_STATE_ASKING 2
+#define DNS_STATE_DONE 3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u8_t flags1);
+ PACK_STRUCT_FIELD(u8_t flags2);
+ PACK_STRUCT_FIELD(u16_t numquestions);
+ PACK_STRUCT_FIELD(u16_t numanswers);
+ PACK_STRUCT_FIELD(u16_t numauthrr);
+ PACK_STRUCT_FIELD(u16_t numextrarr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define SIZEOF_DNS_HDR 12
+
+/** DNS query message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_query {
+ /* DNS query record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_answer {
+ /* DNS answer record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+ u32_t ttl;
+ u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+
+/** DNS table entry */
+struct dns_table_entry {
+ u8_t state;
+ u8_t numdns;
+ u8_t tmr;
+ u8_t retries;
+ u8_t seqno;
+ u8_t err;
+ u32_t ttl;
+ char name[DNS_MAX_NAME_LENGTH];
+ ip_addr_t ipaddr;
+ /* pointer to callback on DNS query done */
+ dns_found_callback found;
+ void *arg;
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ * external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+ DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local();
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+
+/*-----------------------------------------------------------------------------
+ * Globales
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb *dns_pcb;
+static u8_t dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+/** Contiguous buffer for processing responses */
+static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
+static u8_t* dns_payload;
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (DNS_SERVER_ADDRESS).
+ */
+void
+dns_init()
+{
+ ip_addr_t dnsserver;
+
+ dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
+
+ /* initialize default DNS server address */
+ DNS_SERVER_ADDRESS(&dnsserver);
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+ /* if dns client not yet initialized... */
+ if (dns_pcb == NULL) {
+ dns_pcb = udp_new();
+
+ if (dns_pcb != NULL) {
+ /* initialize DNS table not needed (initialized to zero since it is a
+ * global variable) */
+ LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+ DNS_STATE_UNUSED == 0);
+
+ /* initialize DNS client */
+ udp_bind(dns_pcb, IP_ADDR_ANY, 0);
+ udp_recv(dns_pcb, dns_recv, NULL);
+
+ /* initialize default DNS primary server */
+ dns_setserver(0, &dnsserver);
+ }
+ }
+#if DNS_LOCAL_HOSTLIST
+ dns_init_local();
+#endif
+}
+
+/**
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
+{
+ /*
+ * hpa: the lwip code has the dnsserver->addr != 0 test, but that would
+ * seem to indicate that there is no way to cancel a previously given
+ * DNS server...
+ */
+ if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
+ (dnsserver != NULL) /* && !ip_addr_isany(dnsserver) */) {
+ dns_servers[numdns] = (*dnsserver);
+ }
+}
+
+/**
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * server has not been configured.
+ */
+ip_addr_t
+dns_getserver(u8_t numdns)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ return dns_servers[numdns];
+ } else {
+ return *IP_ADDR_ANY;
+ }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+ if (dns_pcb != NULL) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
+ }
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local()
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+ int i;
+ struct local_hostlist_entry *entry;
+ /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+ struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+ size_t namelen;
+ for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
+ struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+ LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+ namelen = strlen(init_entry->name);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+ if (entry != NULL) {
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, init_entry->name, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ entry->addr = init_entry->addr;
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @return The first IP address for the hostname in the local host-list or
+ * IPADDR_NONE if not found.
+ */
+static u32_t
+dns_lookup_local(const char *hostname)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ while(entry != NULL) {
+ if(strcmp(entry->name, hostname) == 0) {
+ return ip4_addr_get_u32(&entry->addr);
+ }
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ int i;
+ for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
+ if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
+ return ip4_addr_get_u32(&local_hostlist_static[i].addr);
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return IPADDR_NONE;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Remove all entries from the local host-list for a specific hostname
+ * and/or IP addess
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ * host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+ int removed = 0;
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ struct local_hostlist_entry *last_entry = NULL;
+ while (entry != NULL) {
+ if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
+ ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+ struct local_hostlist_entry *free_entry;
+ if (last_entry != NULL) {
+ last_entry->next = entry->next;
+ } else {
+ local_hostlist_dynamic = entry->next;
+ }
+ free_entry = entry;
+ entry = entry->next;
+ memp_free(MEMP_LOCALHOSTLIST, free_entry);
+ removed++;
+ } else {
+ last_entry = entry;
+ entry = entry->next;
+ }
+ }
+ return removed;
+}
+
+/**
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+ struct local_hostlist_entry *entry;
+ size_t namelen;
+ LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+ namelen = strlen(hostname);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ if (entry == NULL) {
+ return ERR_MEM;
+ }
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, hostname, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ ip_addr_copy(entry->addr, *addr);
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ * was not found in the cached dns_table.
+ */
+static u32_t
+dns_lookup(const char *name)
+{
+ u8_t i;
+#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
+ u32_t addr;
+#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
+#if DNS_LOCAL_HOSTLIST
+ if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+ if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+ /* Walk through name list, return entry if found. If not, return NULL. */
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ if ((dns_table[i].state == DNS_STATE_DONE) &&
+ (strcmp(name, dns_table[i].name) == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+ ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ return ip4_addr_get_u32(&dns_table[i].ipaddr);
+ }
+ }
+
+ return IPADDR_NONE;
+}
+
+#if DNS_DOES_NAME_CHECK
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param response encoded hostname in the DNS response
+ * @return 0: names equal; 1: names differ
+ */
+static u8_t
+dns_compare_name(unsigned char *query, unsigned char *response)
+{
+ unsigned char n;
+
+ do {
+ n = *response++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ if ((*query) != (*response)) {
+ return 1;
+ }
+ ++response;
+ ++query;
+ --n;
+ };
+ ++query;
+ }
+ } while (*response != 0);
+
+ return 0;
+}
+#endif /* DNS_DOES_NAME_CHECK */
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param query encoded DNS name in the DNS server response
+ * @return end of the name
+ */
+static unsigned char *
+dns_parse_name(unsigned char *query)
+{
+ unsigned char n;
+
+ do {
+ n = *query++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ ++query;
+ --n;
+ };
+ }
+ } while (*query != 0);
+
+ return query + 1;
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param numdns index of the DNS server in the dns_servers table
+ * @param name hostname to query
+ * @param id index of the hostname in dns_table, used as transaction ID in the
+ * DNS query packet
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t numdns, const char* name, u8_t id)
+{
+ err_t err;
+ struct dns_hdr *hdr;
+ struct dns_query qry;
+ struct pbuf *p;
+ char *query, *nptr;
+ const char *pHostname;
+ u8_t n;
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+ (u16_t)(numdns), name));
+ LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
+ LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
+
+ /* if here, we have either a new query or a retry on a previous query to process */
+ p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
+ SIZEOF_DNS_QUERY, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
+ /* fill dns header */
+ hdr = (struct dns_hdr*)p->payload;
+ memset(hdr, 0, SIZEOF_DNS_HDR);
+ hdr->id = htons(id);
+ hdr->flags1 = DNS_FLAG1_RD;
+ hdr->numquestions = PP_HTONS(1);
+ query = (char*)hdr + SIZEOF_DNS_HDR;
+ pHostname = name;
+ --pHostname;
+
+ /* convert hostname into suitable query format. */
+ do {
+ ++pHostname;
+ nptr = query;
+ ++query;
+ for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
+ *query = *pHostname;
+ ++query;
+ ++n;
+ }
+ *nptr = n;
+ } while(*pHostname != 0);
+ *query++='\0';
+
+ /* fill dns query */
+ qry.type = PP_HTONS(DNS_RRTYPE_A);
+ qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+ SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
+
+ /* resize pbuf to the exact dns query */
+ pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
+
+ /* connect to the server for faster receiving */
+ udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
+ /* send dns packet */
+ err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
+
+ /* free pbuf */
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+
+ return err;
+}
+
+/**
+ * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+ err_t err;
+ struct dns_table_entry *pEntry = &dns_table[i];
+
+ LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+ switch(pEntry->state) {
+
+ case DNS_STATE_NEW: {
+ /* initialize new entry */
+ pEntry->state = DNS_STATE_ASKING;
+ pEntry->numdns = 0;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ break;
+ }
+
+ case DNS_STATE_ASKING: {
+ if (--pEntry->tmr == 0) {
+ if (++pEntry->retries == DNS_MAX_RETRIES) {
+ if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
+ /* change of server */
+ pEntry->numdns++;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+ break;
+ } else {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
+ /* call specified callback function if provided */
+ if (pEntry->found)
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ break;
+ }
+ }
+
+ /* wait longer for the next retry */
+ pEntry->tmr = pEntry->retries;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ }
+ break;
+ }
+
+ case DNS_STATE_DONE: {
+ /* if the time to live is nul */
+ if (--pEntry->ttl == 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ }
+ break;
+ }
+ case DNS_STATE_UNUSED:
+ /* nothing to do */
+ break;
+ default:
+ LWIP_ASSERT("unknown dns_table entry state:", 0);
+ break;
+ }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+ u8_t i;
+
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ dns_check_entry(i);
+ }
+}
+
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ *
+ * @params see udp.h
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ u16_t i;
+ char *pHostname;
+ struct dns_hdr *hdr;
+ struct dns_answer ans;
+ struct dns_table_entry *pEntry;
+ u16_t nquestions, nanswers;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ /* is the dns message too big ? */
+ if (p->tot_len > DNS_MSG_SIZE) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* is the dns message big enough ? */
+ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* copy dns payload inside static buffer for processing */
+ if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
+ /* The ID in the DNS header should be our entry into the name table. */
+ hdr = (struct dns_hdr*)dns_payload;
+ i = htons(hdr->id);
+ if (i < DNS_TABLE_SIZE) {
+ pEntry = &dns_table[i];
+ if(pEntry->state == DNS_STATE_ASKING) {
+ /* This entry is now completed. */
+ pEntry->state = DNS_STATE_DONE;
+ pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+
+ /* We only care about the question(s) and the answers. The authrr
+ and the extrarr are simply discarded. */
+ nquestions = htons(hdr->numquestions);
+ nanswers = htons(hdr->numanswers);
+
+ /* Check for error. If so, call callback to inform. */
+ if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+
+#if DNS_DOES_NAME_CHECK
+ /* Check if the name in the "question" part match with the name in the entry. */
+ if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+#endif /* DNS_DOES_NAME_CHECK */
+
+ /* Skip the name in the "question" part */
+ pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
+
+ while (nanswers > 0) {
+ /* skip answer resource record's host name */
+ pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
+
+ /* Check for IP address type and Internet class. Others are discarded. */
+ SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
+ if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
+ (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
+ /* read the answer resource record's TTL, and maximize it if needed */
+ pEntry->ttl = ntohl(ans.ttl);
+ if (pEntry->ttl > DNS_MAX_TTL) {
+ pEntry->ttl = DNS_MAX_TTL;
+ }
+ /* read the IP address after answer resource record's header */
+ SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
+ ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ /* call specified callback function if provided */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
+ }
+ /* deallocate memory and return */
+ goto memerr;
+ } else {
+ pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
+ }
+ --nanswers;
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+ }
+ }
+
+ /* deallocate memory and return */
+ goto memerr;
+
+responseerr:
+ /* ERROR: call specified callback function with NULL as name to indicate an error */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ }
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+
+memerr:
+ /* free pbuf */
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param found a callback founction to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return @return a err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
+{
+ u8_t i;
+ u8_t lseq, lseqi;
+ struct dns_table_entry *pEntry = NULL;
+ size_t namelen;
+
+ /* search an unused entry, or the oldest one */
+ lseq = lseqi = 0;
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ pEntry = &dns_table[i];
+ /* is it an unused entry ? */
+ if (pEntry->state == DNS_STATE_UNUSED)
+ break;
+
+ /* check if this is the oldest completed entry */
+ if (pEntry->state == DNS_STATE_DONE) {
+ if ((dns_seqno - pEntry->seqno) > lseq) {
+ lseq = dns_seqno - pEntry->seqno;
+ lseqi = i;
+ }
+ }
+ }
+
+ /* if we don't have found an unused entry, use the oldest completed one */
+ if (i == DNS_TABLE_SIZE) {
+ if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+ /* no entry can't be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+ return ERR_MEM;
+ } else {
+ /* use the oldest completed one */
+ i = lseqi;
+ pEntry = &dns_table[i];
+ }
+ }
+
+ /* use this entry */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+ /* fill the entry */
+ pEntry->state = DNS_STATE_NEW;
+ pEntry->seqno = dns_seqno++;
+ pEntry->found = found;
+ pEntry->arg = callback_arg;
+ namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
+ MEMCPY(pEntry->name, name, namelen);
+ pEntry->name[namelen] = 0;
+
+ /* force to send query without waiting timer */
+ dns_check_entry(i);
+
+ /* dns query is enqueued */
+ return ERR_INPROGRESS;
+}
+
+/**
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg)
+{
+ u32_t ipaddr;
+ /* not initialized or no valid server yet, or invalid addr pointer
+ * or invalid hostname or invalid hostname length */
+ if ((dns_pcb == NULL) || (addr == NULL) ||
+ (!hostname) || (!hostname[0]) ||
+ (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
+ return ERR_ARG;
+ }
+
+#if LWIP_HAVE_LOOPIF
+ if (strcmp(hostname, "localhost")==0) {
+ ip_addr_set_loopback(addr);
+ return ERR_OK;
+ }
+#endif /* LWIP_HAVE_LOOPIF */
+
+ /* host name already in octet notation? set ip addr and return ERR_OK */
+ ipaddr = ipaddr_addr(hostname);
+ if (ipaddr == IPADDR_NONE) {
+ /* already have this address cached? */
+ ipaddr = dns_lookup(hostname);
+ }
+ if (ipaddr != IPADDR_NONE) {
+ ip4_addr_set_u32(addr, ipaddr);
+ return ERR_OK;
+ }
+
+ /* queue query with specified callback */
+ return dns_enqueue(hostname, found, callback_arg);
+}
+
+#endif /* LWIP_DNS */
diff --git a/core/lwip/src/core/init.c b/core/lwip/src/core/init.c
new file mode 100644
index 00000000..36038350
--- /dev/null
+++ b/core/lwip/src/core/init.c
@@ -0,0 +1,306 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timers.h"
+#include "netif/etharp.h"
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+ #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+ #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && ARP_QUEUEING)
+ #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+ #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+ #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_IGMP)
+ #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+ #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+ #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+ #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+ #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+ #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+ #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+ #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+ #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+ #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))
+ #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+ #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+ #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+ #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+ #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_NETCONN && LWIP_SOCKET)
+ #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+ #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+ #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+ #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+ #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))
+ #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+ #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+ #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+ #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+ #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (TCP_QUEUE_OOSEQ && !LWIP_TCP)
+ #error "TCP_QUEUE_OOSEQ requires LWIP_TCP"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+ #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
+ #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+ #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_IGMP && !defined(LWIP_RAND)
+ #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+ #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+ #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF
+ #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues"
+#endif
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+ #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef MEMP_NUM_API_MSG
+ #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+ #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+ #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+ #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+ #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+
+#ifdef LWIP_DEBUG
+static void
+lwip_sanity_check(void)
+{
+ /* Warnings */
+#if LWIP_NETCONN
+ if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n"));
+#endif /* LWIP_NETCONN */
+#if LWIP_TCP
+ if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n"));
+ if (TCP_SND_BUF < 2 * TCP_MSS)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n"));
+ if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS)))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n"));
+ if (TCP_SNDLOWAT >= TCP_SND_BUF)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF.\n"));
+ if (TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN.\n"));
+ if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n"));
+ if (TCP_WND < TCP_MSS)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n"));
+#endif /* LWIP_TCP */
+#if LWIP_SOCKET
+ /* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
+ if (SO_ACCEPTCONN != SOF_ACCEPTCONN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_ACCEPTCONN != SOF_ACCEPTCONN\n"));
+ if (SO_REUSEADDR != SOF_REUSEADDR)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_REUSEADDR != SOF_REUSEADDR\n"));
+ if (SO_KEEPALIVE != SOF_KEEPALIVE)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_KEEPALIVE != SOF_KEEPALIVE\n"));
+ if (SO_BROADCAST != SOF_BROADCAST)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_BROADCAST != SOF_BROADCAST\n"));
+ if (SO_LINGER != SOF_LINGER)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_LINGER != SOF_LINGER\n"));
+#endif /* LWIP_SOCKET */
+}
+#else /* LWIP_DEBUG */
+#define lwip_sanity_check()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Perform Sanity check of user-configurable values, and initialize all modules.
+ */
+void
+lwip_init(void)
+{
+ /* Sanity check user-configurable values */
+ lwip_sanity_check();
+
+ /* Modules initialization */
+ stats_init();
+#if !NO_SYS
+ sys_init();
+#endif /* !NO_SYS */
+ lwip_mem_init();
+ memp_init();
+ pbuf_init();
+ netif_init();
+#if LWIP_SOCKET
+ lwip_socket_init();
+#endif /* LWIP_SOCKET */
+ ip_init();
+#if LWIP_ARP
+ etharp_init();
+#endif /* LWIP_ARP */
+#if LWIP_RAW
+ raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_SNMP
+ snmp_init();
+#endif /* LWIP_SNMP */
+#if LWIP_AUTOIP
+ autoip_init();
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ dns_init();
+#endif /* LWIP_DNS */
+
+#if LWIP_TIMERS
+ sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
diff --git a/core/lwip/src/core/ipv4/autoip.c b/core/lwip/src/core/ipv4/autoip.c
new file mode 100644
index 00000000..92bb4591
--- /dev/null
+++ b/core/lwip/src/core/ipv4/autoip.c
@@ -0,0 +1,536 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ *
+ * Please coordinate changes and requests with Dominik Spies
+ * <kontakt@dspies.de>
+ */
+
+/*******************************************************************************
+ * USAGE:
+ *
+ * define LWIP_AUTOIP 1 in your lwipopts.h
+ *
+ * If you don't use tcpip.c (so, don't call, you don't call tcpip_init):
+ * - First, call autoip_init().
+ * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces,
+ * that should be defined in autoip.h.
+ * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* 169.254.0.0 */
+#define AUTOIP_NET 0xA9FE0000
+/* 169.254.1.0 */
+#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
+/* 169.254.254.255 */
+#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
+
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+ ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+ ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+ ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+ (netif->autoip?netif->autoip->tried_llipaddr:0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+ htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+ ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static void autoip_handle_arp_conflict(struct netif *netif);
+
+/* creates a pseudo random LL IP-Address for a network interface */
+static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
+
+/* sends an ARP probe */
+static err_t autoip_arp_probe(struct netif *netif);
+
+/* sends an ARP announce */
+static err_t autoip_arp_announce(struct netif *netif);
+
+/* configure interface for use with current LL IP-Address */
+static err_t autoip_bind(struct netif *netif);
+
+/* start sending probes for llipaddr */
+static void autoip_start_probing(struct netif *netif);
+
+/**
+ * Initialize this module
+ */
+void
+autoip_init(void)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n"));
+}
+
+/** Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("autoip != NULL", autoip != NULL);
+ LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL);
+
+ /* clear data structure */
+ memset(autoip, 0, sizeof(struct autoip));
+ /* autoip->state = AUTOIP_STATE_OFF; */
+ netif->autoip = autoip;
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+ netif->autoip->tried_llipaddr++;
+ autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+ /* Somehow detect if we are defending or retreating */
+ unsigned char defend = 1; /* tbd */
+
+ if(defend) {
+ if(netif->autoip->lastconflict > 0) {
+ /* retreat, there was a conflicting ARP in the last
+ * DEFEND_INTERVAL seconds
+ */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+ autoip_arp_announce(netif);
+ netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we do not defend, retreating\n"));
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ * compliant to RFC 3927 Section 2.1
+ * We have 254 * 256 possibilities */
+
+ u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+ addr += netif->autoip->tried_llipaddr;
+ addr = AUTOIP_NET | (addr & 0xffff);
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+ if (addr < AUTOIP_RANGE_START) {
+ addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ if (addr > AUTOIP_RANGE_END) {
+ addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+ (addr <= AUTOIP_RANGE_END));
+ ip4_addr_set_u32(ipaddr, htonl(addr));
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+ ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+ (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, &ethzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, &ethzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ ip_addr_t sn_mask, gw_addr;
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+ IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+ netif_set_ipaddr(netif, &autoip->llipaddr);
+ netif_set_netmask(netif, &sn_mask);
+ netif_set_gw(netif, &gw_addr);
+
+ /* bring the interface up */
+ netif_set_up(netif);
+
+ return ERR_OK;
+}
+
+/**
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ err_t result = ERR_OK;
+
+ if(netif_is_up(netif)) {
+ netif_set_down(netif);
+ }
+
+ /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+ * ARP Packets are formed correctly
+ */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
+ netif->name[1], (u16_t)netif->num));
+ if(autoip == NULL) {
+ /* no AutoIP client attached yet? */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): starting new AUTOIP client\n"));
+ autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+ if(autoip == NULL) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): could not allocate autoip\n"));
+ return ERR_MEM;
+ }
+ memset(autoip, 0, sizeof(struct autoip));
+ /* store this AutoIP client in the netif */
+ netif->autoip = autoip;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+ } else {
+ autoip->state = AUTOIP_STATE_OFF;
+ autoip->ttw = 0;
+ autoip->sent_num = 0;
+ ip_addr_set_zero(&autoip->llipaddr);
+ autoip->lastconflict = 0;
+ }
+
+ autoip_create_addr(netif, &(autoip->llipaddr));
+ autoip_start_probing(netif);
+
+ return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+
+ autoip->state = AUTOIP_STATE_PROBING;
+ autoip->sent_num = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+
+ /* time to wait to first probe, this is randomly
+ * choosen out of 0 to PROBE_WAIT seconds.
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+ /*
+ * if we tried more then MAX_CONFLICTS we must limit our rate for
+ * accquiring and probing address
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ if(autoip->tried_llipaddr > MAX_CONFLICTS) {
+ autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+ if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
+ netif_set_down(netif);
+ autoip_start_probing(netif);
+ }
+}
+
+/**
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+ netif->autoip->state = AUTOIP_STATE_OFF;
+ netif_set_down(netif);
+ return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on AutoIP configured interfaces */
+ if (netif->autoip != NULL) {
+ if(netif->autoip->lastconflict > 0) {
+ netif->autoip->lastconflict--;
+ }
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(netif->autoip->state), netif->autoip->ttw));
+
+ switch(netif->autoip->state) {
+ case AUTOIP_STATE_PROBING:
+ if(netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if(netif->autoip->sent_num >= PROBE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ } else {
+ autoip_arp_probe(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() PROBING Sent Probe\n"));
+ netif->autoip->sent_num++;
+ /* calculate time to wait to next probe */
+ netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+ ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+ PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ }
+ }
+ break;
+
+ case AUTOIP_STATE_ANNOUNCING:
+ if(netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if(netif->autoip->sent_num == 0) {
+ /* We are here the first time, so we waited ANNOUNCE_WAIT seconds
+ * Now we can bind to an IP address and use it.
+ *
+ * autoip_bind calls netif_set_up. This triggers a gratuitous ARP
+ * which counts as an announcement.
+ */
+ autoip_bind(netif);
+ } else {
+ autoip_arp_announce(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+ }
+ netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ netif->autoip->sent_num++;
+
+ if(netif->autoip->sent_num >= ANNOUNCE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_BOUND;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ }
+ }
+ break;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_arp_input.
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+ if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) {
+ /* when ip.src == llipaddr && hw.src != netif->hwaddr
+ *
+ * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
+ * we have a conflict and must solve it
+ */
+ ip_addr_t sipaddr, dipaddr;
+ struct eth_addr netifaddr;
+ ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules).
+ */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ if ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
+ ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
+ (netif->autoip->sent_num == 0))) {
+ /* RFC 3927 Section 2.2.1:
+ * from beginning to after ANNOUNCE_WAIT
+ * seconds we have a conflict if
+ * ip.src == llipaddr OR
+ * ip.dst == llipaddr && hw.src != own hwaddr
+ */
+ if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
+ (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Probe Conflict detected\n"));
+ autoip_restart(netif);
+ }
+ } else {
+ /* RFC 3927 Section 2.5:
+ * in any state we have a conflict if
+ * ip.src == llipaddr && hw.src != own hwaddr
+ */
+ if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+ autoip_handle_arp_conflict(netif);
+ }
+ }
+ }
+}
+
+#endif /* LWIP_AUTOIP */
diff --git a/core/lwip/src/core/ipv4/icmp.c b/core/lwip/src/core/ipv4/icmp.c
new file mode 100644
index 00000000..32902a52
--- /dev/null
+++ b/core/lwip/src/core/ipv4/icmp.c
@@ -0,0 +1,335 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped of when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the ip header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+#ifdef LWIP_DEBUG
+ u8_t code;
+#endif /* LWIP_DEBUG */
+ struct icmp_echo_hdr *iecho;
+ struct ip_hdr *iphdr;
+ s16_t hlen;
+
+ ICMP_STATS_INC(icmp.recv);
+ snmp_inc_icmpinmsgs();
+
+
+ iphdr = (struct ip_hdr *)p->payload;
+ hlen = IPH_HL(iphdr) * 4;
+ if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+ goto lenerr;
+ }
+
+ type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+ code = *(((u8_t *)p->payload)+1);
+#endif /* LWIP_DEBUG */
+ switch (type) {
+ case ICMP_ER:
+ /* This is OK, echo reply might have been parsed by a raw PCB
+ (as obviously, an echo request has been sent, too). */
+ break;
+ case ICMP_ECHO:
+#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+ {
+ int accepted = 1;
+#if !LWIP_MULTICAST_PING
+ /* multicast destination address? */
+ if (ip_addr_ismulticast(&current_iphdr_dest)) {
+ accepted = 0;
+ }
+#endif /* LWIP_MULTICAST_PING */
+#if !LWIP_BROADCAST_PING
+ /* broadcast destination address? */
+ if (ip_addr_isbroadcast(&current_iphdr_dest, inp)) {
+ accepted = 0;
+ }
+#endif /* LWIP_BROADCAST_PING */
+ /* broadcast or multicast destination address not acceptd? */
+ if (!accepted) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
+ ICMP_STATS_INC(icmp.err);
+ pbuf_free(p);
+ return;
+ }
+ }
+#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+ if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+ goto lenerr;
+ }
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.chkerr);
+ snmp_inc_icmpinerrors();
+ return;
+ }
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ /* p is not big enough to contain link headers
+ * allocate a new one and copy p into it
+ */
+ struct pbuf *r;
+ /* switch p->payload to ip header */
+ if (pbuf_header(p, hlen)) {
+ LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
+ goto memerr;
+ }
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+ goto memerr;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
+ (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
+ /* copy the whole packet including ip header */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
+ goto memerr;
+ }
+ iphdr = (struct ip_hdr *)r->payload;
+ /* switch r->payload back to icmp header */
+ if (pbuf_header(r, -hlen)) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ /* free the original p */
+ pbuf_free(p);
+ /* we now have an identical copy of p that has room for link headers */
+ p = r;
+ } else {
+ /* restore p->payload to point to icmp header */
+ if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+ /* At this point, all checks are OK. */
+ /* We generate an answer by switching the dest and src ip addresses,
+ * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+ iecho = (struct icmp_echo_hdr *)p->payload;
+ ip_addr_copy(iphdr->src, *ip_current_dest_addr());
+ ip_addr_copy(iphdr->dest, *ip_current_src_addr());
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+ /* adjust the checksum */
+ if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
+ } else {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
+ }
+
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of echo replies attempted to send */
+ snmp_inc_icmpoutechoreps();
+
+ if(pbuf_header(p, hlen)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ } else {
+ err_t ret;
+ /* send an ICMP packet, src addr is the dest addr of the curren packet */
+ ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
+ ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+ if (ret != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
+ }
+ }
+ break;
+ default:
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+ pbuf_free(p);
+ return;
+lenerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ snmp_inc_icmpinerrors();
+ return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+memerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.err);
+ snmp_inc_icmpinerrors();
+ return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ /* we can use the echo header here */
+ struct icmp_echo_hdr *icmphdr;
+ ip_addr_t iphdr_src;
+
+ /* ICMP header + IP header + 8 bytes of data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+ icmphdr = (struct icmp_echo_hdr *)q->payload;
+ icmphdr->type = type;
+ icmphdr->code = code;
+ icmphdr->id = 0;
+ icmphdr->seqno = 0;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+ /* calculate checksum */
+ icmphdr->chksum = 0;
+ icmphdr->chksum = inet_chksum(icmphdr, q->len);
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of destination unreachable messages attempted to send */
+ snmp_inc_icmpouttimeexcds();
+ ip_addr_copy(iphdr_src, iphdr->src);
+ ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP */
diff --git a/core/lwip/src/core/ipv4/igmp.c b/core/lwip/src/core/ipv4/igmp.c
new file mode 100644
index 00000000..4e4405e1
--- /dev/null
+++ b/core/lwip/src/core/ipv4/igmp.c
@@ -0,0 +1,817 @@
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988 - Host extensions for IP multicasting - V0
+ * RFC 1054 - Host extensions for IP multicasting -
+ * RFC 1112 - Host extensions for IP multicasting - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3 - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
+ * RFC 2113 - IP Router Alert Option -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/stats.h"
+
+#include "string.h"
+
+/*
+ * IGMP constants
+ */
+#define IGMP_TTL 1
+#define IGMP_MINLEN 8
+#define ROUTER_ALERT 0x9404U
+#define ROUTER_ALERTLEN 4
+
+/*
+ * IGMP message types, including version number.
+ */
+#define IGMP_MEMB_QUERY 0x11 /* Membership query */
+#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */
+#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */
+#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
+
+/* Group membership states */
+#define IGMP_GROUP_NON_MEMBER 0
+#define IGMP_GROUP_DELAYING_MEMBER 1
+#define IGMP_GROUP_IDLE_MEMBER 2
+
+/**
+ * IGMP packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct igmp_msg {
+ PACK_STRUCT_FIELD(u8_t igmp_msgtype);
+ PACK_STRUCT_FIELD(u8_t igmp_maxresp);
+ PACK_STRUCT_FIELD(u16_t igmp_checksum);
+ PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
+static err_t igmp_remove_group(struct igmp_group *group);
+static void igmp_timeout( struct igmp_group *group);
+static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void igmp_stop_timer(struct igmp_group *group);
+static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
+static void igmp_send(struct igmp_group *group, u8_t type);
+
+
+static struct igmp_group* igmp_group_list;
+static ip_addr_t allsystems;
+static ip_addr_t allrouters;
+
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+#ifdef LWIP_DEBUG
+/**
+ * Dump global IGMP groups list
+ */
+void
+igmp_dump_group_list()
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+ group = group->next;
+ }
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+}
+#else
+#define igmp_dump_group_list()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+ struct igmp_group* group;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
+
+ group = igmp_lookup_group(netif, &allsystems);
+
+ if (group != NULL) {
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->use++;
+
+ /* Allow the igmp messages at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, &allsystems);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+ struct igmp_group *prev = NULL;
+ struct igmp_group *next;
+
+ /* look for groups joined on this interface further down the list */
+ while (group != NULL) {
+ next = group->next;
+ /* is it a group joined on this interface? */
+ if (group->netif == netif) {
+ /* is it the first group of the list? */
+ if (group == igmp_group_list) {
+ igmp_group_list = next;
+ }
+ /* is there a "previous" group defined? */
+ if (prev != NULL) {
+ prev->next = next;
+ }
+ /* disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+ } else {
+ /* change the "previous" */
+ prev = group;
+ }
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
+
+ while (group != NULL) {
+ if (group->netif == netif) {
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group in the global igmp_group_list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ /* to be clearer, we return NULL here instead of
+ * 'group' (which is also NULL at this point).
+ */
+ return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ * NULL on memory error.
+ */
+struct igmp_group *
+igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ /* Search if the group already exists */
+ group = igmp_lookfor_group(ifp, addr);
+ if (group != NULL) {
+ /* Group already exists. */
+ return group;
+ }
+
+ /* Group doesn't exist yet, create a new one */
+ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+ if (group != NULL) {
+ group->netif = ifp;
+ ip_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = IGMP_GROUP_NON_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+ group->next = igmp_group_list;
+
+ igmp_group_list = group;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
+ ip_addr_debug_print(IGMP_DEBUG, addr);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
+
+ return group;
+}
+
+/**
+ * Remove a group in the global igmp_group_list
+ *
+ * @param group the group to remove from the global igmp_group_list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct igmp_group *group)
+{
+ err_t err = ERR_OK;
+
+ /* Is it the first group? */
+ if (igmp_group_list == group) {
+ igmp_group_list = group->next;
+ } else {
+ /* look for group further down the list */
+ struct igmp_group *tmpGroup;
+ for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ if (tmpGroup->next == group) {
+ tmpGroup->next = group->next;
+ break;
+ }
+ }
+ /* Group not found in the global igmp_group_list */
+ if (tmpGroup == NULL)
+ err = ERR_ARG;
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+
+ return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the ip header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
+{
+ struct ip_hdr * iphdr;
+ struct igmp_msg* igmp;
+ struct igmp_group* group;
+ struct igmp_group* groupref;
+
+ IGMP_STATS_INC(igmp.recv);
+
+ /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.lenerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+ ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
+
+ /* Now calculate and check the checksum */
+ igmp = (struct igmp_msg *)p->payload;
+ if (inet_chksum(igmp, p->len)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.chkerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+ return;
+ }
+
+ /* Packet is ok so find an existing group */
+ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+ /* If group can be found or create... */
+ if (!group) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.drop);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+ return;
+ }
+
+ /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+ switch (igmp->igmp_msgtype) {
+ case IGMP_MEMB_QUERY: {
+ /* IGMP_MEMB_QUERY to the "all systems" address ? */
+ if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
+ /* THIS IS THE GENERAL QUERY */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+ if (igmp->igmp_maxresp == 0) {
+ IGMP_STATS_INC(igmp.rx_v1);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+ igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+ } else {
+ IGMP_STATS_INC(igmp.rx_general);
+ }
+
+ groupref = igmp_group_list;
+ while (groupref) {
+ /* Do not send messages on the all systems group address! */
+ if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
+ igmp_delaying_member(groupref, igmp->igmp_maxresp);
+ }
+ groupref = groupref->next;
+ }
+ } else {
+ /* IGMP_MEMB_QUERY to a specific group ? */
+ if (!ip_addr_isany(&igmp->igmp_group_address)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+ ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
+ if (ip_addr_cmp(dest, &allsystems)) {
+ ip_addr_t groupaddr;
+ LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ /* we first need to re-look for the group since we used dest last time */
+ ip_addr_copy(groupaddr, igmp->igmp_group_address);
+ group = igmp_lookfor_group(inp, &groupaddr);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ }
+
+ if (group != NULL) {
+ IGMP_STATS_INC(igmp.rx_group);
+ igmp_delaying_member(group, igmp->igmp_maxresp);
+ } else {
+ IGMP_STATS_INC(igmp.drop);
+ }
+ } else {
+ IGMP_STATS_INC(igmp.proterr);
+ }
+ }
+ break;
+ }
+ case IGMP_V2_MEMB_REPORT: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+ IGMP_STATS_INC(igmp.rx_report);
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* This is on a specific group we have already looked up */
+ group->timer = 0; /* stopped */
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ break;
+ }
+ default: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+ igmp->igmp_msgtype, group->group_state, &group, group->netif));
+ IGMP_STATS_INC(igmp.proterr);
+ break;
+ }
+ }
+
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we join this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group or create a new one if not found */
+ group = igmp_lookup_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* This should create a new group, check the state to make sure */
+ if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+ } else {
+ /* OK - it was new group */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If first use of the group, allow the group at the MAC level */
+ if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
+ }
+
+ IGMP_STATS_INC(igmp.tx_join);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+
+ igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+ /* Need to work out where this timer comes from */
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+ /* Increment group use */
+ group->use++;
+ /* Join on this interface */
+ err = ERR_OK;
+ } else {
+ /* Return an error even if some network interfaces are joined */
+ /** @todo undo any other netif already joined */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
+ return ERR_MEM;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we leave this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group */
+ group = igmp_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Only send a leave if the flag is set according to the state diagram */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If there is no other use of the group */
+ if (group->use <= 1) {
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
+ IGMP_STATS_INC(igmp.tx_leave);
+ igmp_send(group, IGMP_LEAVE_GROUP);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* Free the group */
+ igmp_remove_group(group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+ /* Leave on this interface */
+ err = ERR_OK;
+ } else {
+ /* It's not a fatal error on "leavegroup" */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ igmp_timeout(group);
+ }
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct igmp_group *group)
+{
+ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+
+ IGMP_STATS_INC(igmp.tx_report);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+ }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ * every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+ /* ensure the input value is > 0 */
+ if (max_time == 0) {
+ max_time = 1;
+ }
+ /* ensure the random value is > 0 */
+ group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
+}
+
+/**
+ * Stop a timer for an igmp_group
+ *
+ * @param group the igmp_group for which to stop the timer
+ */
+static void
+igmp_stop_timer(struct igmp_group *group)
+{
+ group->timer = 0;
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+ if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ igmp_start_timer(group, maxresp);
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
+{
+ /* This is the "router alert" option */
+ u16_t ra[2];
+ ra[0] = PP_HTONS(ROUTER_ALERT);
+ ra[1] = 0x0000; /* Router shall examine packet */
+ IGMP_STATS_INC(igmp.xmit);
+ return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct igmp_group *group, u8_t type)
+{
+ struct pbuf* p = NULL;
+ struct igmp_msg* igmp = NULL;
+ ip_addr_t src = *IP_ADDR_ANY;
+ ip_addr_t* dest = NULL;
+
+ /* IP header + "router alert" option + IGMP header */
+ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+ if (p) {
+ igmp = (struct igmp_msg *)p->payload;
+ LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+ (p->len >= sizeof(struct igmp_msg)));
+ ip_addr_copy(src, group->netif->ip_addr);
+
+ if (type == IGMP_V2_MEMB_REPORT) {
+ dest = &(group->group_address);
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ group->last_reporter_flag = 1; /* Remember we were the last to report */
+ } else {
+ if (type == IGMP_LEAVE_GROUP) {
+ dest = &allrouters;
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ }
+ }
+
+ if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+ igmp->igmp_msgtype = type;
+ igmp->igmp_maxresp = 0;
+ igmp->igmp_checksum = 0;
+ igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+ igmp_ip_output_if(p, &src, dest, group->netif);
+ }
+
+ pbuf_free(p);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+ IGMP_STATS_INC(igmp.memerr);
+ }
+}
+
+#endif /* LWIP_IGMP */
diff --git a/core/lwip/src/core/ipv4/inet.c b/core/lwip/src/core/ipv4/inet.c
new file mode 100644
index 00000000..e283a576
--- /dev/null
+++ b/core/lwip/src/core/ipv4/inet.c
@@ -0,0 +1,42 @@
+/**
+ * @file
+ * Functions common to all TCP/IPv4 modules, such as the byte order functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet.h"
+
diff --git a/core/lwip/src/core/ipv4/inet_chksum.c b/core/lwip/src/core/ipv4/inet_chksum.c
new file mode 100644
index 00000000..960252f6
--- /dev/null
+++ b/core/lwip/src/core/ipv4/inet_chksum.c
@@ -0,0 +1,450 @@
+/**
+ * @file
+ * Incluse internet checksum functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+
+#include <stddef.h>
+#include <string.h>
+
+/* These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * #define LWIP_CHKSUM <your_checksum_routine>
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 2
+# endif
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+static u16_t
+lwip_standard_chksum(void *dataptr, u16_t len)
+{
+ u32_t acc;
+ u16_t src;
+ u8_t *octetptr;
+
+ acc = 0;
+ /* dataptr may be at odd or even addresses */
+ octetptr = (u8_t*)dataptr;
+ while (len > 1) {
+ /* declare first octet as most significant
+ thus assume network order, ignoring host order */
+ src = (*octetptr) << 8;
+ octetptr++;
+ /* declare second octet as least significant */
+ src |= (*octetptr);
+ octetptr++;
+ acc += src;
+ len -= 2;
+ }
+ if (len > 0) {
+ /* accumulate remaining octet */
+ src = (*octetptr) << 8;
+ acc += src;
+ }
+ /* add deferred carry bits */
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ if ((acc & 0xffff0000UL) != 0) {
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ }
+ /* This maybe a little confusing: reorder sum using htons()
+ instead of ntohs() since it has a little less call overhead.
+ The caller must invert bits for Internet sum ! */
+ return htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+
+static u16_t
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t sum = 0;
+ int odd = ((mem_ptr_t)pb & 1);
+
+ /* Get aligned to u16_t */
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ /* Add the bulk of the data */
+ ps = (u16_t *)(void *)pb;
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* Consume left-over byte, if any */
+ if (len > 0) {
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ /* Add end bytes */
+ sum += t;
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ /* Swap if alignment was odd */
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+
+static u16_t
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t *pl;
+ u32_t sum = 0, tmp;
+ /* starts at odd byte address? */
+ int odd = ((mem_ptr_t)pb & 1);
+
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ ps = (u16_t *)pb;
+
+ if (((mem_ptr_t)ps & 3) && len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ pl = (u32_t *)ps;
+
+ while (len > 7) {
+ tmp = sum + *pl++; /* ping */
+ if (tmp < sum) {
+ tmp++; /* add back carry */
+ }
+
+ sum = tmp + *pl++; /* pong */
+ if (sum < tmp) {
+ sum++; /* add back carry */
+ }
+
+ len -= 8;
+ }
+
+ /* make room in upper bits */
+ sum = FOLD_U32T(sum);
+
+ ps = (u16_t *)pl;
+
+ /* 16-bit aligned word remaining? */
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* dangling tail byte remaining? */
+ if (len > 0) { /* include odd byte */
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ sum += t; /* add end bytes */
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len)
+{
+ u32_t acc;
+ u32_t addr;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ /* iterate through all pbuf in chain */
+ for(q = p; q != NULL; q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* just executing this next line is probably faster that the if statement needed
+ to check whether we really need to execute it, and does no harm */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ addr = ip4_addr_get_u32(src);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len, u16_t chksum_len)
+{
+ u32_t acc;
+ u32_t addr;
+ struct pbuf *q;
+ u8_t swapped;
+ u16_t chklen;
+
+ acc = 0;
+ swapped = 0;
+ /* iterate through all pbuf in chain */
+ for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ chklen = q->len;
+ if (chklen > chksum_len) {
+ chklen = chksum_len;
+ }
+ acc += LWIP_CHKSUM(q->payload, chklen);
+ chksum_len -= chklen;
+ LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* fold the upper bit down */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ addr = ip4_addr_get_u32(src);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+ return ~LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ for(q = p; q != NULL; q = q->next) {
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+ MEMCPY(dst, src, len);
+ return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
diff --git a/core/lwip/src/core/ipv4/ip.c b/core/lwip/src/core/ipv4/ip.c
new file mode 100644
index 00000000..6f248716
--- /dev/null
+++ b/core/lwip/src/core/ipv4/ip.c
@@ -0,0 +1,857 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include <string.h>
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#define LWIP_INLINE_IP_CHKSUM 1
+#endif
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE 1
+#else
+#define CHECKSUM_GEN_IP_INLINE 0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
+ || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/**
+ * The interface that provided the packet for the current callback
+ * invocation.
+ */
+struct netif *current_netif;
+
+/**
+ * Header of the input packet currently being processed.
+ */
+const struct ip_hdr *current_header;
+/** Source IP address of current_header */
+ip_addr_t current_iphdr_src;
+/** Destination IP address of current_header */
+ip_addr_t current_iphdr_dest;
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip_route(ip_addr_t *dest)
+{
+ struct netif *netif;
+
+ /* iterate through netifs */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ /* network mask matches? */
+ if (netif_is_up(netif)) {
+ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+ }
+ if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ snmp_inc_ipoutnoroutes();
+ return NULL;
+ }
+ /* no matching netif found, use default netif */
+ return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+
+ PERF_START;
+
+ /* RFC3927 2.7: do not forward link-local addresses */
+ if (ip_addr_islinklocal(&current_iphdr_dest)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
+ ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
+ goto return_noroute;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip_route(&current_iphdr_dest);
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+ ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
+ ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
+ goto return_noroute;
+ }
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
+ goto return_noroute;
+ }
+
+ /* decrement TTL */
+ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+ /* send ICMP if TTL == 0 */
+ if (IPH_TTL(iphdr) == 0) {
+ snmp_inc_ipinhdrerrors();
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ return;
+ }
+
+ /* Incrementally update the IP checksum. */
+ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ } else {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ }
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
+ ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
+
+ IP_STATS_INC(ip.fw);
+ IP_STATS_INC(ip.xmit);
+ snmp_inc_ipforwdatagrams();
+
+ PERF_STOP("ip_forward");
+ /* transmit pbuf on chosen interface */
+ netif->output(netif, p, &current_iphdr_dest);
+ return;
+return_noroute:
+ snmp_inc_ipoutnoroutes();
+}
+#endif /* IP_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip_hdr *iphdr;
+ struct netif *netif;
+ u16_t iphdr_hlen;
+ u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ IP_STATS_INC(ip.recv);
+ snmp_inc_ipinreceives();
+
+ /* identify the IP header */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (IPH_V(iphdr) != 4) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+
+ /* obtain IP header length in number of 32-bit words */
+ iphdr_hlen = IPH_HL(iphdr);
+ /* calculate IP header length in bytes */
+ iphdr_hlen *= 4;
+ /* obtain ip length in bytes */
+ iphdr_len = ntohs(IPH_LEN(iphdr));
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
+ if (iphdr_hlen > p->len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_hlen, p->len));
+ }
+ if (iphdr_len > p->tot_len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.lenerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+
+ /* verify checksum */
+#if CHECKSUM_CHECK_IP
+ if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.chkerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+#endif
+
+ /* Trim pbuf. This should have been done at the netif layer,
+ * but we'll do it anyway just to be sure that its done. */
+ pbuf_realloc(p, iphdr_len);
+
+ /* copy IP addresses to aligned ip_addr_t */
+ ip_addr_copy(current_iphdr_dest, iphdr->dest);
+ ip_addr_copy(current_iphdr_src, iphdr->src);
+
+ /* match packet against an interface, i.e. is this packet for us? */
+#if LWIP_IGMP
+ if (ip_addr_ismulticast(&current_iphdr_dest)) {
+ if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &current_iphdr_dest))) {
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+ } else
+#endif /* LWIP_IGMP */
+ {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+ ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr),
+ ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask),
+ ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask),
+ ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask)));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
+ /* unicast to this interface address? */
+ if (ip_addr_cmp(&current_iphdr_dest, &(netif->ip_addr)) ||
+ /* or broadcast on this interface network address? */
+ ip_addr_isbroadcast(&current_iphdr_dest, netif)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if ((netif->autoip != NULL) &&
+ ip_addr_cmp(&current_iphdr_dest, &(netif->autoip->llipaddr))) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ if (first) {
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while(netif != NULL);
+ }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+ * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+ * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+ *
+ * If you want to accept private broadcast communication while a netif is down,
+ * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+ *
+ * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+ */
+ if (netif == NULL) {
+ /* remote port is DHCP server? */
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
+ ntohs(udphdr->dest)));
+ if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n"));
+ netif = inp;
+ check_ip_src = 0;
+ }
+ }
+ }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+ if (check_ip_src && !ip_addr_isany(&current_iphdr_src))
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ { if ((ip_addr_isbroadcast(&current_iphdr_src, inp)) ||
+ (ip_addr_ismulticast(&current_iphdr_src))) {
+ /* packet source is not valid */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n"));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
+#if IP_FORWARD
+ /* non-broadcast packet? */
+ if (!ip_addr_isbroadcast(&current_iphdr_dest, inp)) {
+ /* try to forward IP packet on (other) interfaces */
+ ip_forward(p, iphdr, inp);
+ } else
+#endif /* IP_FORWARD */
+ {
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ }
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ /* packet consists of multiple fragments? */
+ if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
+ ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
+ /* reassemble the packet*/
+ p = ip_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ return ERR_OK;
+ }
+ iphdr = (struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+ ntohs(IPH_OFFSET(iphdr))));
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+#endif /* IP_REASSEMBLY */
+ }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+ /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+ if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+ if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+ pbuf_free(p);
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+ }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
+ ip_debug_print(p);
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ current_netif = inp;
+ current_header = iphdr;
+
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+
+ switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ snmp_inc_ipindelivers();
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ snmp_inc_ipindelivers();
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ snmp_inc_ipindelivers();
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, &current_iphdr_dest);
+ break;
+#endif /* LWIP_IGMP */
+ default:
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable unless is was a broadcast */
+ if (!ip_addr_isbroadcast(&current_iphdr_dest, inp) &&
+ !ip_addr_ismulticast(&current_iphdr_dest)) {
+ p->payload = iphdr;
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinunknownprotos();
+ }
+ }
+
+ current_netif = NULL;
+ current_header = NULL;
+ ip_addr_set_any(&current_iphdr_src);
+ ip_addr_set_any(&current_iphdr_dest);
+
+ return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ * unique identifiers independent of destination"
+ */
+err_t
+ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ struct ip_hdr *iphdr;
+ ip_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+ u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ snmp_inc_ipoutrequests();
+
+ /* Should the IP header be generated or is it already included in p? */
+ if (dest != IP_HDRINCL) {
+ u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+ u16_t optlen_aligned = 0;
+ if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+ int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ /* round up to a multiple of 4 */
+ optlen_aligned = ((optlen + 3) & ~3);
+ ip_hlen += optlen_aligned;
+ /* First write in the IP options */
+ if (pbuf_header(p, optlen_aligned)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+ MEMCPY(p->payload, ip_options, optlen);
+ if (optlen < optlen_aligned) {
+ /* zero the remaining bytes */
+ memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+ }
+#if CHECKSUM_GEN_IP_INLINE
+ for (i = 0; i < optlen_aligned/2; i++) {
+ chk_sum += ((u16_t*)p->payload)[i];
+ }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ }
+#endif /* IP_OPTIONS_SEND */
+ /* generate IP header */
+ if (pbuf_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
+
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+ (p->len >= sizeof(struct ip_hdr)));
+
+ IPH_TTL_SET(iphdr, ttl);
+ IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += LWIP_MAKE_U16(proto, ttl);
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* dest cannot be NULL here */
+ ip_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_v_hl_tos;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_LEN_SET(iphdr, htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_OFFSET_SET(iphdr, 0);
+ IPH_ID_SET(iphdr, htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ ++ip_id;
+
+ if (ip_addr_isany(src)) {
+ ip_addr_copy(iphdr->src, netif->ip_addr);
+ } else {
+ /* src cannot be NULL here */
+ ip_addr_copy(iphdr->src, *src);
+ }
+
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+ chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+ chk_sum = (chk_sum >> 16) + chk_sum;
+ chk_sum = ~chk_sum;
+ iphdr->_chksum = chk_sum; /* network order */
+#else /* CHECKSUM_GEN_IP_INLINE */
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+#endif
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ } else {
+ /* IP header already included in p */
+ iphdr = (struct ip_hdr *)p->payload;
+ ip_addr_copy(dest_addr, iphdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+ ip_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ if (ip_addr_cmp(dest, &netif->ip_addr)) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
+ return netif_loop_output(netif, p, dest);
+ }
+#if LWIP_IGMP
+ if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+ netif_loop_output(netif, p, dest);
+ }
+#endif /* LWIP_IGMP */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ return ip_frag(p, netif, dest);
+ }
+#endif /* IP_FRAG */
+
+ LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));
+ return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto)
+{
+ struct netif *netif;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ netif->addr_hint = addr_hint;
+ err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
+ netif->addr_hint = NULL;
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+ u8_t *payload;
+
+ payload = (u8_t *)iphdr + IP_HLEN;
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
+ IPH_V(iphdr),
+ IPH_HL(iphdr),
+ IPH_TOS(iphdr),
+ ntohs(IPH_LEN(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
+ ntohs(IPH_ID(iphdr)),
+ ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
+ ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
+ IPH_TTL(iphdr),
+ IPH_PROTO(iphdr),
+ ntohs(IPH_CHKSUM(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
+ ip4_addr1_16(&iphdr->src),
+ ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src),
+ ip4_addr4_16(&iphdr->src)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n",
+ ip4_addr1_16(&iphdr->dest),
+ ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest),
+ ip4_addr4_16(&iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
diff --git a/core/lwip/src/core/ipv4/ip_addr.c b/core/lwip/src/core/ipv4/ip_addr.c
new file mode 100644
index 00000000..8f633ff2
--- /dev/null
+++ b/core/lwip/src/core/ipv4/ip_addr.c
@@ -0,0 +1,312 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = { IPADDR_ANY };
+const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST };
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)
+{
+ ip_addr_t ipaddr;
+ ip4_addr_set_u32(&ipaddr, addr);
+
+ /* all ones (broadcast) or all zeroes (old skool broadcast) */
+ if ((~addr == IPADDR_ANY) ||
+ (addr == IPADDR_ANY)) {
+ return 1;
+ /* no broadcast support on this network interface? */
+ } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+ /* the given address cannot be a broadcast address
+ * nor can we check against any broadcast addresses */
+ return 0;
+ /* address matches network interface address exactly? => no broadcast */
+ } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) {
+ return 0;
+ /* on the same (sub) network... */
+ } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask))
+ /* ...and host identifier bits are all ones? =>... */
+ && ((addr & ~ip4_addr_get_u32(&netif->netmask)) ==
+ (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) {
+ /* => network broadcast address */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+ u32_t mask;
+ u32_t nm_hostorder = lwip_htonl(netmask);
+
+ /* first, check for the first zero */
+ for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) == 0) {
+ break;
+ }
+ }
+ /* then check that there is no one */
+ for (; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) != 0) {
+ /* there is a one after the first zero -> invalid */
+ return 0;
+ }
+ }
+ /* no one after the first zero -> valid */
+ return 1;
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+#define isdigit(c) in_range(c, '0', '9')
+#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+ ip_addr_t val;
+
+ if (ipaddr_aton(cp, &val)) {
+ return ip4_addr_get_u32(&val);
+ }
+ return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+ u32_t val;
+ u8_t base;
+ char c;
+ u32_t parts[4];
+ u32_t *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, 1-9=decimal.
+ */
+ if (!isdigit(c))
+ return (0);
+ val = 0;
+ base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = *++cp;
+ } else
+ base = 8;
+ }
+ for (;;) {
+ if (isdigit(c)) {
+ val = (val * base) + (int)(c - '0');
+ c = *++cp;
+ } else if (base == 16 && isxdigit(c)) {
+ val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3) {
+ return (0);
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !isspace(c)) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ switch (pp - parts + 1) {
+
+ case 0:
+ return (0); /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffffUL) {
+ return (0);
+ }
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ LWIP_ASSERT("unhandled", 0);
+ break;
+ }
+ if (addr) {
+ ip4_addr_set_u32(addr, htonl(val));
+ }
+ return (1);
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * represenation of addr
+ */
+char *
+ipaddr_ntoa(const ip_addr_t *addr)
+{
+ static char str[16];
+ return ipaddr_ntoa_r(addr, str, 16);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ u32_t s_addr;
+ char inv[3];
+ char *rp;
+ u8_t *ap;
+ u8_t rem;
+ u8_t n;
+ u8_t i;
+ int len = 0;
+
+ s_addr = ip4_addr_get_u32(addr);
+
+ rp = buf;
+ ap = (u8_t *)&s_addr;
+ for(n = 0; n < 4; n++) {
+ i = 0;
+ do {
+ rem = *ap % (u8_t)10;
+ *ap /= (u8_t)10;
+ inv[i++] = '0' + rem;
+ } while(*ap);
+ while(i--) {
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = inv[i];
+ }
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = '.';
+ ap++;
+ }
+ *--rp = 0;
+ return buf;
+}
diff --git a/core/lwip/src/core/ipv4/ip_frag.c b/core/lwip/src/core/ipv4/ip_frag.c
new file mode 100644
index 00000000..8d184345
--- /dev/null
+++ b/core/lwip/src/core/ipv4/ip_frag.c
@@ -0,0 +1,863 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u8_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ snmp_inc_ipreasmfails();
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount -= pbufs_freed;
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata*
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata* ipr;
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reass struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param root_p points to the 'root' pbuf for the current datagram being assembled.
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct pbuf *q;
+ u16_t offset,len;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr*)new_p->payload;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper*)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = offset + len;
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find on with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if(iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+ } else if(iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no wholes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+ /* and had no wholes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
+ valid = 0;
+ } else {
+ /* and check that there are no wholes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+ iprh->end == ipr->datagram_len);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+ ip_reass_pbufcount -= pbuf_clen(new_p);
+ pbuf_free(new_p);
+ return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len;
+ u8_t clen;
+ struct ip_reassdata *ipr_prev = NULL;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ snmp_inc_ipreasmreqds();
+
+ fraghdr = (struct ip_hdr*)p->payload;
+
+ if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+ ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ ipr_prev = ipr;
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if(ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip_reass_pbufcount += clen;
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ ipr->datagram_len = offset + len;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ ipr->datagram_len += IP_HLEN;
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr*)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set calculate the correct checksum? */
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while(r != NULL) {
+ iprh = (struct ip_reass_helper*)r->payload;
+
+ /* hide the ip header for every succeding fragment */
+ pbuf_header(r, -IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ ip_reass_pbufcount -= pbuf_clen(p);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if IP_FRAG_USES_STATIC_BUF
+static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
+#else /* IP_FRAG_USES_STATIC_BUF */
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by using a fixed size static memory buffer (PBUF_REF) or
+ * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if IP_FRAG_USES_STATIC_BUF
+ struct pbuf *header;
+#else
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+#endif
+ struct ip_hdr *original_iphdr;
+#endif
+ struct ip_hdr *iphdr;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu = netif->mtu;
+ u16_t ofo, omf;
+ u16_t last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+
+ /* Get a RAM based MTU sized pbuf */
+#if IP_FRAG_USES_STATIC_BUF
+ /* When using a static buffer, we use a PBUF_REF, which we will
+ * use to reference the packet (without link header).
+ * Layer and length is irrelevant.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
+ if (rambuf == NULL) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
+ return ERR_MEM;
+ }
+ rambuf->tot_len = rambuf->len = mtu;
+ rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
+
+ /* Copy the IP header in it */
+ iphdr = (struct ip_hdr *)rambuf->payload;
+ SMEMCPY(iphdr, p->payload, IP_HLEN);
+#else /* IP_FRAG_USES_STATIC_BUF */
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Save original offset */
+ tmp = ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ omf = tmp & IP_MF;
+
+ left = p->tot_len - IP_HLEN;
+
+ nfb = (mtu - IP_HLEN) / 8;
+
+ while (left) {
+ last = (left <= mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = omf | (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
+ }
+
+ /* Fill this fragment */
+ cop = last ? left : nfb * 8;
+
+#if IP_FRAG_USES_STATIC_BUF
+ poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
+#else /* IP_FRAG_USES_STATIC_BUF */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
+ /* make room for the IP header */
+ if(pbuf_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Correct header */
+ IPH_OFFSET_SET(iphdr, htons(tmp));
+ IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
+ IPH_CHKSUM_SET(iphdr, 0);
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+
+#if IP_FRAG_USES_STATIC_BUF
+ if (last) {
+ pbuf_realloc(rambuf, left + IP_HLEN);
+ }
+
+ /* This part is ugly: we alloc a RAM based pbuf for
+ * the link level header for each chunk and then
+ * free it.A PBUF_ROM style pbuf for which pbuf_header
+ * worked would make things simpler.
+ */
+ header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+ if (header != NULL) {
+ pbuf_chain(header, rambuf);
+ netif->output(netif, header, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+ snmp_inc_ipfragcreates();
+ pbuf_free(header);
+ } else {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+#else /* IP_FRAG_USES_STATIC_BUF */
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ left -= cop;
+ ofo += nfb;
+ }
+#if IP_FRAG_USES_STATIC_BUF
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ snmp_inc_ipfragoks();
+ return ERR_OK;
+}
+#endif /* IP_FRAG */
diff --git a/core/lwip/src/core/mem.c b/core/lwip/src/core/mem.c
new file mode 100644
index 00000000..2934e5a9
--- /dev/null
+++ b/core/lwip/src/core/mem.c
@@ -0,0 +1,642 @@
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include <string.h>
+
+#if MEM_USE_POOLS
+/* lwIP head implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ struct memp_malloc_helper *element;
+ memp_t poolnr;
+ mem_size_t required_size = size + sizeof(struct memp_malloc_helper);
+
+ for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+again:
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ /* is this pool big enough to hold an element of the required size
+ plus a struct memp_malloc_helper that saves the pool this element came from? */
+ if (required_size <= memp_sizes[poolnr]) {
+ break;
+ }
+ }
+ if (poolnr > MEMP_POOL_LAST) {
+ LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+ return NULL;
+ }
+ element = (struct memp_malloc_helper*)memp_malloc(poolnr);
+ if (element == NULL) {
+ /* No need to DEBUGF or ASSERT: This error is already
+ taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+ /** Try a bigger pool if this one is empty! */
+ if (poolnr < MEMP_POOL_LAST) {
+ poolnr++;
+ goto again;
+ }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ return NULL;
+ }
+
+ /* save the pool number this element came from */
+ element->poolnr = poolnr;
+ /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+ element++;
+
+ return element;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+ struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem;
+
+ LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+ LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+ /* get the original struct memp_malloc_helper */
+ hmem--;
+
+ LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+ LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+ LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+ /* and put it in the pool we saved earlier */
+ memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
+ */
+struct mem {
+ /** index (-> ram[next]) of the next struct */
+ mem_size_t next;
+ /** index (-> ram[prev]) of the previous struct */
+ mem_size_t prev;
+ /** 1: this area is used; 0: this area is unused */
+ u8_t used;
+};
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem *lfree;
+
+/** concurrent access protection */
+static sys_mutex_t mem_mutex;
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc)
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a semaphore */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void
+plug_holes(struct mem *mem)
+{
+ struct mem *nmem;
+ struct mem *pmem;
+
+ LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+ LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+ LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+ /* plug hole forward */
+ LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+ nmem = (struct mem *)(void *)&ram[mem->next];
+ if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+ /* if mem->next is unused and not end of ram, combine mem and mem->next */
+ if (lfree == nmem) {
+ lfree = mem;
+ }
+ mem->next = nmem->next;
+ ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
+ }
+
+ /* plug hole backward */
+ pmem = (struct mem *)(void *)&ram[mem->prev];
+ if (pmem != mem && pmem->used == 0) {
+ /* if mem->prev is unused, combine mem and mem->prev */
+ if (lfree == mem) {
+ lfree = pmem;
+ }
+ pmem->next = mem->next;
+ ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
+ }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+lwip_mem_init(void)
+{
+ struct mem *mem;
+
+ LWIP_ASSERT("Sanity check alignment",
+ (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+
+ /* align the heap */
+ ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+ /* initialize the start of the heap */
+ mem = (struct mem *)(void *)ram;
+ mem->next = MEM_SIZE_ALIGNED;
+ mem->prev = 0;
+ mem->used = 0;
+ /* initialize the end of the heap */
+ ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
+ ram_end->used = 1;
+ ram_end->next = MEM_SIZE_ALIGNED;
+ ram_end->prev = MEM_SIZE_ALIGNED;
+
+ /* initialize the lowest-free pointer to the start of the heap */
+ lfree = (struct mem *)(void *)ram;
+
+ MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+ if(sys_mutex_new(&mem_mutex) != ERR_OK) {
+ LWIP_ASSERT("failed to create mem_mutex", 0);
+ }
+}
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ * call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+ struct mem *mem;
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ if (rmem == NULL) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+ return;
+ }
+ LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
+
+ LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... which has to be in a used state ... */
+ LWIP_ASSERT("mem_free: mem->used", mem->used);
+ /* ... and is now unused. */
+ mem->used = 0;
+
+ if (mem < lfree) {
+ /* the newly freed struct is now the lowest */
+ lfree = mem;
+ }
+
+ MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+ /* finally, see if prev or next are free also */
+ plug_holes(mem);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
+ * @param newsize required size after shrinking (needs to be smaller than or
+ * equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ * or NULL if newsize is > old size, in which case rmem is NOT touched
+ * or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t newsize)
+{
+ mem_size_t size;
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+ /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+
+ if(newsize < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ newsize = MIN_SIZE_ALIGNED;
+ }
+
+ if (newsize > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return rmem;
+ }
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... and its offset pointer */
+ ptr = (mem_size_t)((u8_t *)mem - ram);
+
+ size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+ LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+ if (newsize > size) {
+ /* not supported */
+ return NULL;
+ }
+ if (newsize == size) {
+ /* No change in size, simply return */
+ return rmem;
+ }
+
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+
+ mem2 = (struct mem *)(void *)&ram[mem->next];
+ if(mem2->used == 0) {
+ /* The next struct is unused, we can simply move it at little */
+ mem_size_t next;
+ /* remember the old next pointer */
+ next = mem2->next;
+ /* create new struct mem which is moved directly after the shrinked mem */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ if (lfree == mem2) {
+ lfree = (struct mem *)(void *)&ram[ptr2];
+ }
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ /* restore the next pointer */
+ mem2->next = next;
+ /* link it back to mem */
+ mem2->prev = ptr;
+ /* link mem to it */
+ mem->next = ptr2;
+ /* last thing to restore linked list: as we have moved mem2,
+ * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+ * the end of the heap */
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* no need to plug holes, we've already done that */
+ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+ /* Next struct is used but there's room for another struct mem with
+ * at least MIN_SIZE_ALIGNED of data.
+ * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+ * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ if (mem2 < lfree) {
+ lfree = mem2;
+ }
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ mem->next = ptr2;
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* the original mem->next is used, so no need to plug holes! */
+ }
+ /* else {
+ next struct mem is used but size between mem and mem2 is not big enough
+ to create another struct mem
+ -> don't do anyhting.
+ -> the remaining space stays unused since it is too small
+ } */
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+ return rmem;
+}
+
+/**
+ * Adam's mem_malloc() plus solution for bug #17922
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_ALLOC_DECL_PROTECT();
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ size = LWIP_MEM_ALIGN_SIZE(size);
+
+ if(size < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ size = MIN_SIZE_ALIGNED;
+ }
+
+ if (size > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ /* protect the heap from concurrent access */
+ sys_mutex_lock(&mem_mutex);
+ LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* run as long as a mem_free disturbed mem_malloc */
+ do {
+ local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ /* Scan through the heap searching for a free block that is big enough,
+ * beginning with the lowest free block.
+ */
+ for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
+ ptr = ((struct mem *)(void *)&ram[ptr])->next) {
+ mem = (struct mem *)(void *)&ram[ptr];
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* allow mem_free to run */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ local_mem_free_count = mem_free_count;
+ }
+ mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ if ((!mem->used) &&
+ (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+ /* mem is not used and at least perfect fit is possible:
+ * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+ /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+ * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+ * -> split large block, create empty remainder,
+ * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+ * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+ * struct mem would fit in but no data between mem2 and mem2->next
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory
+ */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+ /* create mem2 struct */
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ /* and insert it between mem and mem->next */
+ mem->next = ptr2;
+ mem->used = 1;
+
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+ } else {
+ /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+ * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+ * take care of this).
+ * -> near fit or excact fit: do not split, no mem2 creation
+ * also can't move mem->next directly behind mem, since mem->next
+ * will always be used at this point!
+ */
+ mem->used = 1;
+ MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
+ }
+
+ if (mem == lfree) {
+ /* Find next free block after mem and update lowest free pointer */
+ while (lfree->used && lfree != ram_end) {
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* prevent high interrupt latency... */
+ LWIP_MEM_ALLOC_PROTECT();
+ lfree = (struct mem *)(void *)&ram[lfree->next];
+ }
+ LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+ }
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+ (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+ LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+ ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+ LWIP_ASSERT("mem_malloc: sanity check alignment",
+ (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
+
+ return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+ }
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* if we got interrupted by a mem_free, try again */
+ } while(local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+ MEM_STATS_INC(err);
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *mem_calloc(mem_size_t count, mem_size_t size)
+{
+ void *p;
+
+ /* allocate 'count' objects of size 'size' */
+ p = mem_malloc(count * size);
+ if (p) {
+ /* zero the memory */
+ memset(p, 0, count * size);
+ }
+ return p;
+}
+
+#endif /* !MEM_LIBC_MALLOC */
diff --git a/core/lwip/src/core/memp.c b/core/lwip/src/core/memp.c
new file mode 100644
index 00000000..4da879a5
--- /dev/null
+++ b/core/lwip/src/core/memp.c
@@ -0,0 +1,469 @@
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+#include "lwip/api_msg.h"
+#include "lwip/tcpip.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/stats.h"
+#include "netif/etharp.h"
+#include "lwip/ip_frag.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/dns.h"
+#include "netif/ppp_oe.h"
+
+#include <string.h>
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+struct memp {
+ struct memp *next;
+#if MEMP_OVERFLOW_CHECK
+ const char *file;
+ int line;
+#endif /* MEMP_OVERFLOW_CHECK */
+};
+
+#if MEMP_OVERFLOW_CHECK
+/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning
+ * and at the end of each element, initialize them as 0xcd and check
+ * them later. */
+/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free,
+ * every single element in each pool is checked!
+ * This is VERY SLOW but also very helpful. */
+/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in
+ * lwipopts.h to change the amount reserved for checking. */
+#ifndef MEMP_SANITY_REGION_BEFORE
+#define MEMP_SANITY_REGION_BEFORE 16
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#if MEMP_SANITY_REGION_BEFORE > 0
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE)
+#else
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#ifndef MEMP_SANITY_REGION_AFTER
+#define MEMP_SANITY_REGION_AFTER 16
+#endif /* MEMP_SANITY_REGION_AFTER*/
+#if MEMP_SANITY_REGION_AFTER > 0
+#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER)
+#else
+#define MEMP_SANITY_REGION_AFTER_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_AFTER*/
+
+/* MEMP_SIZE: save space for struct memp and for sanity check */
+#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED)
+
+#else /* MEMP_OVERFLOW_CHECK */
+
+/* No sanity checks
+ * We don't need to preserve the struct memp while not allocated, so we
+ * can save a little space and set MEMP_SIZE to 0.
+ */
+#define MEMP_SIZE 0
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/** This array holds the first free element of each pool.
+ * Elements form a linked list. */
+static struct memp *memp_tab[MEMP_MAX];
+
+#else /* MEMP_MEM_MALLOC */
+
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_MEM_MALLOC */
+
+/** This array holds the element sizes of each pool. */
+#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
+static
+#endif
+const u16_t memp_sizes[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
+#include "lwip/memp_std.h"
+};
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+/** This array holds the number of elements in each pool. */
+static const u16_t memp_num[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) (num),
+#include "lwip/memp_std.h"
+};
+
+/** This array holds a textual description of each pool. */
+#ifdef LWIP_DEBUG
+static const char *memp_desc[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) (desc),
+#include "lwip/memp_std.h"
+};
+#endif /* LWIP_DEBUG */
+
+#if MEMP_SEPARATE_POOLS
+
+/** This creates each memory pool. These are named memp_memory_XXX_base (where
+ * XXX is the name of the pool defined in memp_std.h).
+ * To relocate a pool, declare it as extern in cc.h. Example for GCC:
+ * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[];
+ */
+#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \
+ [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))];
+#include "lwip/memp_std.h"
+
+/** This array holds the base of each memory pool. */
+static u8_t *const memp_bases[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base,
+#include "lwip/memp_std.h"
+};
+
+#else /* MEMP_SEPARATE_POOLS */
+
+/** This is the actual memory used by the pools (all pools in one big block). */
+static u8_t memp_memory[MEM_ALIGNMENT - 1
+#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
+#include "lwip/memp_std.h"
+];
+
+#endif /* MEMP_SEPARATE_POOLS */
+
+#if MEMP_SANITY_CHECK
+/**
+ * Check that memp-lists don't form a circle
+ */
+static int
+memp_sanity(void)
+{
+ s16_t i, c;
+ struct memp *m, *n;
+
+ for (i = 0; i < MEMP_MAX; i++) {
+ for (m = memp_tab[i]; m != NULL; m = m->next) {
+ c = 1;
+ for (n = memp_tab[i]; n != NULL; n = n->next) {
+ if (n == m && --c < 0) {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+#endif /* MEMP_SANITY_CHECK*/
+#if MEMP_OVERFLOW_CHECK
+#if defined(LWIP_DEBUG) && MEMP_STATS
+static const char * memp_overflow_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) "/"desc,
+#include "lwip/memp_std.h"
+ };
+#endif
+
+/**
+ * Check if a memp element was victim of an overflow
+ * (e.g. the restricted area after it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void
+memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type];
+ for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp overflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Check if a memp element was victim of an underflow
+ * (e.g. the restricted area before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void
+memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp underflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void
+memp_overflow_check_all(void)
+{
+ u16_t i, j;
+ struct memp *p;
+
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_overflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_underflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+
+/**
+ * Initialize the restricted areas of all memp elements in every pool.
+ */
+static void
+memp_overflow_init(void)
+{
+ u16_t i, j;
+ struct memp *p;
+ u8_t *m;
+
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[i];
+ memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+#endif
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize this module.
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+ struct memp *memp;
+ u16_t i, j;
+
+ for (i = 0; i < MEMP_MAX; ++i) {
+ MEMP_STATS_AVAIL(used, i, 0);
+ MEMP_STATS_AVAIL(max, i, 0);
+ MEMP_STATS_AVAIL(err, i, 0);
+ MEMP_STATS_AVAIL(avail, i, memp_num[i]);
+ }
+
+#if !MEMP_SEPARATE_POOLS
+ memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+#endif /* !MEMP_SEPARATE_POOLS */
+ /* for every pool: */
+ for (i = 0; i < MEMP_MAX; ++i) {
+ memp_tab[i] = NULL;
+#if MEMP_SEPARATE_POOLS
+ memp = (struct memp*)memp_bases[i];
+#endif /* MEMP_SEPARATE_POOLS */
+ /* create a linked list of memp elements */
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp->next = memp_tab[i];
+ memp_tab[i] = memp;
+ memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+ );
+ }
+ }
+#if MEMP_OVERFLOW_CHECK
+ memp_overflow_init();
+ /* check everything a first time to see if it worked */
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK */
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * the debug version has two more parameters:
+ * @param file file name calling this function
+ * @param line number of line where this function is called
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char* file, const int line)
+#endif
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+ memp = memp_tab[type];
+
+ if (memp != NULL) {
+ memp_tab[type] = memp->next;
+#if MEMP_OVERFLOW_CHECK
+ memp->next = NULL;
+ memp->file = file;
+ memp->line = line;
+#endif /* MEMP_OVERFLOW_CHECK */
+ MEMP_STATS_INC_USED(used, type);
+ LWIP_ASSERT("memp_malloc: memp properly aligned",
+ ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+ memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
+ } else {
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
+ MEMP_STATS_INC(err, type);
+ }
+
+ SYS_ARCH_UNPROTECT(old_level);
+
+ return memp;
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ if (mem == NULL) {
+ return;
+ }
+ LWIP_ASSERT("memp_free: mem properly aligned",
+ ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+ memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#else
+ memp_overflow_check_element_overflow(memp, type);
+ memp_overflow_check_element_underflow(memp, type);
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+ MEMP_STATS_DEC(used, type);
+
+ memp->next = memp_tab[type];
+ memp_tab[type] = memp;
+
+#if MEMP_SANITY_CHECK
+ LWIP_ASSERT("memp sanity", memp_sanity());
+#endif /* MEMP_SANITY_CHECK */
+
+ SYS_ARCH_UNPROTECT(old_level);
+}
+
+#endif /* MEMP_MEM_MALLOC */
diff --git a/core/lwip/src/core/netif.c b/core/lwip/src/core/netif.c
new file mode 100644
index 00000000..f190b1f2
--- /dev/null
+++ b/core/lwip/src/core/netif.c
@@ -0,0 +1,752 @@
+/**
+ * @file
+ * lwIP network interface abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "netif/etharp.h"
+#include "lwip/stats.h"
+#if ENABLE_LOOPBACK
+#include "lwip/sys.h"
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+struct netif *netif_list;
+struct netif *netif_default;
+
+#if LWIP_HAVE_LOOPIF
+static struct netif loop_netif;
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ */
+static err_t
+netif_loopif_init(struct netif *netif)
+{
+ /* initialize the snmp variables and counters inside the struct netif
+ * ifSpeed: no assumption can be made!
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0);
+
+ netif->name[0] = 'l';
+ netif->name[1] = 'o';
+ netif->output = netif_loop_output;
+ return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+ ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
+ IP4_ADDR(&loop_gw, 127,0,0,1);
+ IP4_ADDR(&loop_ipaddr, 127,0,0,1);
+ IP4_ADDR(&loop_netmask, 255,0,0,0);
+
+#if NO_SYS
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
+#else /* NO_SYS */
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+ netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @param netif a pre-allocated netif structure
+ * @param ipaddr IP address for the new netif
+ * @param netmask network mask for the new netif
+ * @param gw default gateway IP address for the new netif
+ * @param state opaque data passed to the new netif
+ * @param init callback function that initializes the interface
+ * @param input callback function that is called to pass
+ * ingress packets up in the protocol layer stack.
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
+{
+ static u8_t netifnum = 0;
+
+ LWIP_ASSERT("No init function given", init != NULL);
+
+ /* reset new interface configuration state */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+ netif->flags = 0;
+#if LWIP_DHCP
+ /* netif not under DHCP control by default */
+ netif->dhcp = NULL;
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ /* netif not under AutoIP control by default */
+ netif->autoip = NULL;
+#endif /* LWIP_AUTOIP */
+#if LWIP_NETIF_STATUS_CALLBACK
+ netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+ netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if ENABLE_LOOPBACK
+ netif->loop_first = NULL;
+ netif->loop_last = NULL;
+#endif /* ENABLE_LOOPBACK */
+
+ /* remember netif specific state information data */
+ netif->state = state;
+ netif->num = netifnum++;
+ netif->input = input;
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+ netif->loop_cnt_current = 0;
+#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+
+ netif_set_addr(netif, ipaddr, netmask, gw);
+
+ /* call user specified initialization function for netif */
+ if (init(netif) != ERR_OK) {
+ return NULL;
+ }
+
+ /* add this netif to the list */
+ netif->next = netif_list;
+ netif_list = netif;
+ snmp_inc_iflist();
+
+#if LWIP_IGMP
+ /* start IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_start(netif);
+ }
+#endif /* LWIP_IGMP */
+
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
+ netif->name[0], netif->name[1]));
+ ip_addr_debug_print(NETIF_DEBUG, ipaddr);
+ LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+ ip_addr_debug_print(NETIF_DEBUG, netmask);
+ LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+ ip_addr_debug_print(NETIF_DEBUG, gw);
+ LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+ return netif;
+}
+
+/**
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ netif_set_ipaddr(netif, ipaddr);
+ netif_set_netmask(netif, netmask);
+ netif_set_gw(netif, gw);
+}
+
+/**
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+ if (netif == NULL) {
+ return;
+ }
+
+#if LWIP_IGMP
+ /* stop IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_stop(netif);
+ }
+#endif /* LWIP_IGMP */
+ if (netif_is_up(netif)) {
+ /* set netif down before removing (call callback function) */
+ netif_set_down(netif);
+ }
+
+ snmp_delete_ipaddridx_tree(netif);
+
+ /* is it the first netif? */
+ if (netif_list == netif) {
+ netif_list = netif->next;
+ } else {
+ /* look for netif further down the list */
+ struct netif * tmpNetif;
+ for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) {
+ if (tmpNetif->next == netif) {
+ tmpNetif->next = netif->next;
+ break;
+ }
+ }
+ if (tmpNetif == NULL)
+ return; /* we didn't find any netif today */
+ }
+ snmp_dec_iflist();
+ /* this netif is default? */
+ if (netif_default == netif) {
+ /* reset default netif */
+ netif_set_default(NULL);
+ }
+ LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+/**
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(char *name)
+{
+ struct netif *netif;
+ u8_t num;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ num = name[2] - '0';
+
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ if (num == netif->num &&
+ name[0] == netif->name[0] &&
+ name[1] == netif->name[1]) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+ return netif;
+ }
+ }
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+ return NULL;
+}
+
+/**
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* TODO: Handling of obsolete pcbs */
+ /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
+#if LWIP_TCP
+ struct tcp_pcb *pcb;
+ struct tcp_pcb_listen *lpcb;
+
+ /* address is actually being changed? */
+ if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
+ /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+ pcb = tcp_active_pcbs;
+ while (pcb != NULL) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+ && !ip_addr_islinklocal(&(pcb->local_ip))
+#endif /* LWIP_AUTOIP */
+ ) {
+ /* this connection must be aborted */
+ struct tcp_pcb *next = pcb->next;
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+ tcp_abort(pcb);
+ pcb = next;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ /* PCB bound to current local interface address? */
+ if ((!(ip_addr_isany(&(lpcb->local_ip)))) &&
+ (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) {
+ /* The PCB is listening to the old ipaddr and
+ * is set to listen to the new one instead */
+ ip_addr_set(&(lpcb->local_ip), ipaddr);
+ }
+ }
+ }
+#endif
+ snmp_delete_ipaddridx_tree(netif);
+ snmp_delete_iprteidx_tree(0,netif);
+ /* set new IP address to netif */
+ ip_addr_set(&(netif->ip_addr), ipaddr);
+ snmp_insert_ipaddridx_tree(netif);
+ snmp_insert_iprteidx_tree(0,netif);
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->ip_addr),
+ ip4_addr2_16(&netif->ip_addr),
+ ip4_addr3_16(&netif->ip_addr),
+ ip4_addr4_16(&netif->ip_addr)));
+}
+
+/**
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, ip_addr_t *gw)
+{
+ ip_addr_set(&(netif->gw), gw);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->gw),
+ ip4_addr2_16(&netif->gw),
+ ip4_addr3_16(&netif->gw),
+ ip4_addr4_16(&netif->gw)));
+}
+
+/**
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, ip_addr_t *netmask)
+{
+ snmp_delete_iprteidx_tree(0, netif);
+ /* set new netmask to netif */
+ ip_addr_set(&(netif->netmask), netmask);
+ snmp_insert_iprteidx_tree(0, netif);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->netmask),
+ ip4_addr2_16(&netif->netmask),
+ ip4_addr3_16(&netif->netmask),
+ ip4_addr4_16(&netif->netmask)));
+}
+
+/**
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+ if (netif == NULL) {
+ /* remove default route */
+ snmp_delete_iprteidx_tree(1, netif);
+ } else {
+ /* install default route */
+ snmp_insert_iprteidx_tree(1, netif);
+ }
+ netif_default = netif;
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * Bring an interface up, available for processing
+ * traffic.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_up(struct netif *netif)
+{
+ if (!(netif->flags & NETIF_FLAG_UP)) {
+ netif->flags |= NETIF_FLAG_UP;
+
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif /* LWIP_SNMP */
+
+ NETIF_STATUS_CALLBACK(netif);
+
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & (NETIF_FLAG_ETHARP)) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+ }
+}
+
+/**
+ * Bring an interface down, disabling any traffic processing.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_down(struct netif *netif)
+{
+ if (netif->flags & NETIF_FLAG_UP) {
+ netif->flags &= ~NETIF_FLAG_UP;
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif
+
+ NETIF_STATUS_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * Set callback to be called when interface is brought up/down
+ */
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+ if (netif) {
+ netif->status_callback = status_callback;
+ }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+/**
+ * Called by a driver when its link goes up
+ */
+void netif_set_link_up(struct netif *netif )
+{
+ if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+ netif->flags |= NETIF_FLAG_LINK_UP;
+
+#if LWIP_DHCP
+ if (netif->dhcp) {
+ dhcp_network_changed(netif);
+ }
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+ if (netif->autoip) {
+ autoip_network_changed(netif);
+ }
+#endif /* LWIP_AUTOIP */
+
+ if (netif->flags & NETIF_FLAG_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+/**
+ * Called by a driver when its link goes down
+ */
+void netif_set_link_down(struct netif *netif )
+{
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ netif->flags &= ~NETIF_FLAG_LINK_UP;
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * Set callback to be called when link is brought up/down
+ */
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+ if (netif) {
+ netif->link_callback = link_callback;
+ }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is simply copied and handed back to netif->input.
+ * In multithreaded mode, this is done directly since netif->input must put
+ * the packet on a queue.
+ * In callback mode, the packet is put on an internal queue and is fed to
+ * netif->input by netif_poll().
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @param ipaddr the ip address to send the packet to (not used)
+ * @return ERR_OK if the packet has been sent
+ * ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p,
+ ip_addr_t *ipaddr)
+{
+ struct pbuf *r;
+ err_t err;
+ struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_UNUSED_ARG(ipaddr);
+
+ /* Allocate a new pbuf */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen = pbuf_clen(r);
+ /* check for overflow or too many pbuf on queue */
+ if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+ ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+ netif->loop_cnt_current += clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* Copy the whole pbuf queue p into the single pbuf r */
+ if ((err = pbuf_copy(r, p)) != ERR_OK) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return err;
+ }
+
+ /* Put the packet on a linked list which gets emptied through calling
+ netif_poll(). */
+
+ /* let last point to the last pbuf in chain r */
+ for (last = r; last->next != NULL; last = last->next);
+
+ SYS_ARCH_PROTECT(lev);
+ if(netif->loop_first != NULL) {
+ LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+ netif->loop_last->next = r;
+ netif->loop_last = last;
+ } else {
+ netif->loop_first = r;
+ netif->loop_last = last;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ LINK_STATS_INC(link.xmit);
+ snmp_add_ifoutoctets(stats_if, p->tot_len);
+ snmp_inc_ifoutucastpkts(stats_if);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+ /* For multithreading environment, schedule a call to netif_poll */
+ tcpip_callback((tcpip_callback_fn)netif_poll, netif);
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+ return ERR_OK;
+}
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+ struct pbuf *in;
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ do {
+ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+ SYS_ARCH_PROTECT(lev);
+ in = netif->loop_first;
+ if (in != NULL) {
+ struct pbuf *in_end = in;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = pbuf_clen(in);
+ /* adjust the number of pbufs on queue */
+ LWIP_ASSERT("netif->loop_cnt_current underflow",
+ ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+ netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ while (in_end->len != in_end->tot_len) {
+ LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+ in_end = in_end->next;
+ }
+ /* 'in_end' now points to the last pbuf from 'in' */
+ if (in_end == netif->loop_last) {
+ /* this was the last pbuf in the list */
+ netif->loop_first = netif->loop_last = NULL;
+ } else {
+ /* pop the pbuf off the list */
+ netif->loop_first = in_end->next;
+ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+ }
+ /* De-queue the pbuf from its successors on the 'loop_' list. */
+ in_end->next = NULL;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ if (in != NULL) {
+ LINK_STATS_INC(link.recv);
+ snmp_add_ifinoctets(stats_if, in->tot_len);
+ snmp_inc_ifinucastpkts(stats_if);
+ /* loopback packets are always IP packets! */
+ if (ip_input(in, netif) != ERR_OK) {
+ pbuf_free(in);
+ }
+ /* Don't reference the packet any more! */
+ in = NULL;
+ }
+ /* go on while there is a packet on the list */
+ } while (netif->loop_first != NULL);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netifs */
+ while (netif != NULL) {
+ netif_poll(netif);
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
diff --git a/core/lwip/src/core/pbuf.c b/core/lwip/src/core/pbuf.c
new file mode 100644
index 00000000..dd9ff64e
--- /dev/null
+++ b/core/lwip/src/core/pbuf.c
@@ -0,0 +1,1156 @@
+/**
+ * @file
+ * Packet buffer management
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "arch/perf.h"
+#if TCP_QUEUE_OOSEQ
+#include "lwip/tcp_impl.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+ aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
+#ifndef PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_FREE_OOSEQ 1
+#endif /* PBUF_POOL_FREE_OOSEQ */
+
+#if PBUF_POOL_FREE_OOSEQ
+#include "lwip/tcpip.h"
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+static u8_t pbuf_free_ooseq_queued;
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+static void
+pbuf_free_ooseq(void* arg)
+{
+ struct tcp_pcb* pcb;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ LWIP_UNUSED_ARG(arg);
+
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_queued = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+ if (NULL != pcb->ooseq) {
+ /** Free the ooseq pbufs of one PCB only */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ return;
+ }
+ }
+}
+
+/** Queue a call to pbuf_free_ooseq if not already queued. */
+static void
+pbuf_pool_is_empty(void)
+{
+ u8_t queued;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ SYS_ARCH_PROTECT(old_level);
+ queued = pbuf_free_ooseq_queued;
+ pbuf_free_ooseq_queued = 1;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ if(!queued) {
+ /* queue a call to pbuf_free_ooseq if not already queued */
+ if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) {
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_queued = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+ }
+}
+#endif /* PBUF_POOL_FREE_OOSEQ */
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+
+/**
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ * chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. Additional headers must be prepended
+ * by allocating another pbuf and chain in to the front of
+ * the ROM pbuf. It is assumed that the memory used is really
+ * similar to ROM in that it is immutable and will not be
+ * changed. Memory which is dynamic should generally not
+ * be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. It is assumed that the pbuf is only
+ * being used in a single thread. If the pbuf gets queued,
+ * then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ * the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+ struct pbuf *p, *q, *r;
+ u16_t offset;
+ s32_t rem_len; /* remaining length */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ offset = 0;
+ switch (layer) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset += PBUF_TRANSPORT_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset += PBUF_IP_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset += PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW:
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ switch (type) {
+ case PBUF_POOL:
+ /* allocate head of pbuf chain into p */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
+ if (p == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ return NULL;
+ }
+ p->type = type;
+ p->next = NULL;
+
+ /* make the payload pointer point 'offset' bytes into pbuf data memory */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
+ LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ /* the total length of the pbuf chain is the requested size */
+ p->tot_len = length;
+ /* set the length of the first pbuf in the chain */
+ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+ (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+ /* set reference count (needed here in case we fail) */
+ p->ref = 1;
+
+ /* now allocate the tail of the pbuf chain */
+
+ /* remember first pbuf for linkage in next iteration */
+ r = p;
+ /* remaining length to be allocated */
+ rem_len = length - p->len;
+ /* any remaining pbufs to be allocated? */
+ while (rem_len > 0) {
+ q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ if (q == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ /* free chain so far allocated */
+ pbuf_free(p);
+ /* bail out unsuccesfully */
+ return NULL;
+ }
+ q->type = type;
+ q->flags = 0;
+ q->next = NULL;
+ /* make previous pbuf point to this pbuf */
+ r->next = q;
+ /* set total length of this pbuf and next in chain */
+ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
+ q->tot_len = (u16_t)rem_len;
+ /* this pbuf length is pool size, unless smaller sized tail */
+ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
+ q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
+ LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+ ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ q->ref = 1;
+ /* calculate remaining length to be allocated */
+ rem_len -= q->len;
+ /* remember this pbuf for linkage in next iteration */
+ r = q;
+ }
+ /* end of chain */
+ /*r->next = NULL;*/
+
+ break;
+ case PBUF_RAM:
+ /* If pbuf is to be allocated in RAM, allocate memory for it. */
+ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
+ if (p == NULL) {
+ return NULL;
+ }
+ /* Set up internal structure of the pbuf. */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+
+ LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ break;
+ /* pbuf references existing (non-volatile static constant) ROM payload? */
+ case PBUF_ROM:
+ /* pbuf references existing (externally allocated) RAM payload? */
+ case PBUF_REF:
+ /* only allocate memory for the pbuf structure */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF);
+ if (p == NULL) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ /* caller must set this field properly, afterwards */
+ p->payload = NULL;
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+ return NULL;
+ }
+ /* set reference count */
+ p->ref = 1;
+ /* set flags */
+ p->flags = 0;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+ return p;
+}
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Initialize a custom pbuf (already allocated).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ * this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ * must be at least big enough to hold 'length' plus the header size,
+ * may be NULL if set later
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ * big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+ void *payload_mem, u16_t payload_mem_len)
+{
+ u16_t offset;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ offset = 0;
+ switch (l) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset += PBUF_TRANSPORT_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset += PBUF_IP_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset += PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW:
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+ return NULL;
+ }
+
+ p->pbuf.next = NULL;
+ if (payload_mem != NULL) {
+ p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset));
+ } else {
+ p->pbuf.payload = NULL;
+ }
+ p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+ p->pbuf.len = p->pbuf.tot_len = length;
+ p->pbuf.type = type;
+ p->pbuf.ref = 1;
+ return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+ struct pbuf *q;
+ u16_t rem_len; /* remaining length */
+ s32_t grow;
+
+ LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+ LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+ p->type == PBUF_ROM ||
+ p->type == PBUF_RAM ||
+ p->type == PBUF_REF);
+
+ /* desired length larger than current length? */
+ if (new_len >= p->tot_len) {
+ /* enlarging not yet supported */
+ return;
+ }
+
+ /* the pbuf chain grows by (new_len - p->tot_len) bytes
+ * (which may be negative in case of shrinking) */
+ grow = new_len - p->tot_len;
+
+ /* first, step over any pbufs that should remain in the chain */
+ rem_len = new_len;
+ q = p;
+ /* should this pbuf be kept? */
+ while (rem_len > q->len) {
+ /* decrease remaining length by pbuf length */
+ rem_len -= q->len;
+ /* decrease total length indicator */
+ LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+ q->tot_len += (u16_t)grow;
+ /* proceed to next pbuf in chain */
+ q = q->next;
+ LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+ }
+ /* we have now reached the new last pbuf (in q) */
+ /* rem_len == desired length for pbuf q */
+
+ /* shrink allocated memory for PBUF_RAM */
+ /* (other types merely adjust their length fields */
+ if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
+ /* reallocate and adjust the length of the pbuf that will be split */
+ q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+ LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+ }
+ /* adjust length fields for new last pbuf */
+ q->len = rem_len;
+ q->tot_len = q->len;
+
+ /* any remaining pbufs in chain? */
+ if (q->next != NULL) {
+ /* free remaining pbufs in chain */
+ pbuf_free(q->next);
+ }
+ /* q is last packet in chain */
+ q->next = NULL;
+
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+ u16_t type;
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_increment == 0) || (p == NULL)) {
+ return 0;
+ }
+
+ if (header_size_increment < 0){
+ increment_magnitude = -header_size_increment;
+ /* Check that we aren't going to move off the end of the pbuf */
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+ } else {
+ increment_magnitude = header_size_increment;
+#if 0
+ /* Can't assert these as some callers speculatively call
+ pbuf_header() to see if it's OK. Will return 1 below instead. */
+ /* Check that we've got the correct type of pbuf to work with */
+ LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
+ p->type == PBUF_RAM || p->type == PBUF_POOL);
+ /* Check that we aren't going to move off the beginning of the pbuf */
+ LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
+ (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
+#endif
+ }
+
+ type = p->type;
+ /* remember current payload pointer */
+ payload = p->payload;
+
+ /* pbuf types containing payloads? */
+ if (type == PBUF_RAM || type == PBUF_POOL) {
+ /* set new payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ /* boundary check fails? */
+ if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+ (void *)p->payload, (void *)(p + 1)));
+ /* restore old payload pointer */
+ p->payload = payload;
+ /* bail out unsuccesfully */
+ return 1;
+ }
+ /* pbuf types refering to external payloads? */
+ } else if (type == PBUF_REF || type == PBUF_ROM) {
+ /* hide a header in the payload? */
+ if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else {
+ /* cannot expand payload to front (yet!)
+ * bail out unsuccesfully */
+ return 1;
+ }
+ } else {
+ /* Unknown type */
+ LWIP_ASSERT("bad pbuf type", 0);
+ return 1;
+ }
+ /* modify pbuf length fields */
+ p->len += header_size_increment;
+ p->tot_len += header_size_increment;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
+ (void *)payload, (void *)p->payload, header_size_increment));
+
+ return 0;
+}
+
+/**
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ *
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+ u16_t type;
+ struct pbuf *q;
+ u8_t count;
+
+ if (p == NULL) {
+ LWIP_ASSERT("p != NULL", p != NULL);
+ /* if assertions are disabled, proceed with debug output */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_free(p == NULL) was called.\n"));
+ return 0;
+ }
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+ PERF_START;
+
+ LWIP_ASSERT("pbuf_free: sane type",
+ p->type == PBUF_RAM || p->type == PBUF_ROM ||
+ p->type == PBUF_REF || p->type == PBUF_POOL);
+
+ count = 0;
+ /* de-allocate all consecutive pbufs from the head of the chain that
+ * obtain a zero reference count after decrementing*/
+ while (p != NULL) {
+ u16_t ref;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* Since decrementing ref cannot be guaranteed to be a single machine operation
+ * we must protect it. We put the new ref into a local variable to prevent
+ * further protection. */
+ SYS_ARCH_PROTECT(old_level);
+ /* all pbufs in a chain are referenced at least once */
+ LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+ /* decrease reference count (number of pointers to pbuf) */
+ ref = --(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ /* this pbuf is no longer referenced to? */
+ if (ref == 0) {
+ /* remember next pbuf in chain for next iteration */
+ q = p->next;
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+ type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+ /* is this a custom pbuf? */
+ if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+ struct pbuf_custom *pc = (struct pbuf_custom*)p;
+ LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+ pc->custom_free_function(p);
+ } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+ {
+ /* is this a pbuf from the pool? */
+ if (type == PBUF_POOL) {
+ memp_free(MEMP_PBUF_POOL, p);
+ /* is this a ROM or RAM referencing pbuf? */
+ } else if (type == PBUF_ROM || type == PBUF_REF) {
+ memp_free(MEMP_PBUF, p);
+ /* type == PBUF_RAM */
+ } else {
+ mem_free(p);
+ }
+ }
+ count++;
+ /* proceed to next pbuf */
+ p = q;
+ /* p->ref > 0, this pbuf is still referenced to */
+ /* (and so the remaining pbufs in chain as well) */
+ } else {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+ /* stop walking through the chain */
+ p = NULL;
+ }
+ }
+ PERF_STOP("pbuf_free");
+ /* return number of de-allocated pbufs */
+ return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+
+u8_t
+pbuf_clen(struct pbuf *p)
+{
+ u8_t len;
+
+ len = 0;
+ while (p != NULL) {
+ ++len;
+ p = p->next;
+ }
+ return len;
+}
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* pbuf given? */
+ if (p != NULL) {
+ SYS_ARCH_PROTECT(old_level);
+ ++(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+}
+
+/**
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+ struct pbuf *p;
+
+ LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+ ((h != NULL) && (t != NULL)), return;);
+
+ /* proceed to last pbuf of chain */
+ for (p = h; p->next != NULL; p = p->next) {
+ /* add total length of second chain to all totals of first chain */
+ p->tot_len += t->tot_len;
+ }
+ /* { p is last pbuf of first h chain, p->next == NULL } */
+ LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+ LWIP_ASSERT("p->next == NULL", p->next == NULL);
+ /* add total length of second chain to last pbuf total of first chain */
+ p->tot_len += t->tot_len;
+ /* chain last pbuf of head (p) with first of tail (t) */
+ p->next = t;
+ /* p->next now references t, but the caller will drop its reference to t,
+ * so netto there is no change to the reference count of t.
+ */
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+ pbuf_cat(h, t);
+ /* t is now referenced by h */
+ pbuf_ref(t);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+ struct pbuf *q;
+ u8_t tail_gone = 1;
+ /* tail */
+ q = p->next;
+ /* pbuf has successor in chain? */
+ if (q != NULL) {
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+ /* enforce invariant if assertion is disabled */
+ q->tot_len = p->tot_len - p->len;
+ /* decouple pbuf from remainder */
+ p->next = NULL;
+ /* total length of pbuf p is its own length only */
+ p->tot_len = p->len;
+ /* q is no longer referenced by p, free it */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+ tail_gone = pbuf_free(q);
+ if (tail_gone > 0) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+ }
+ /* return remaining tail or NULL if deallocated */
+ }
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+ return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ *
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ * ERR_ARG if one of the pbufs is NULL or p_to is not big
+ * enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+{
+ u16_t offset_to=0, offset_from=0, len;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+ (void*)p_to, (void*)p_from));
+
+ /* is the target big enough to hold the source? */
+ LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+ (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+ /* iterate through pbuf chain */
+ do
+ {
+ LWIP_ASSERT("p_to != NULL", p_to != NULL);
+ /* copy one part of the original chain */
+ if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+ /* complete current p_from fits into current p_to */
+ len = p_from->len - offset_from;
+ } else {
+ /* current p_from does not fit into current p_to */
+ len = p_to->len - offset_to;
+ }
+ MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+ offset_to += len;
+ offset_from += len;
+ LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+ if (offset_to == p_to->len) {
+ /* on to next p_to (if any) */
+ offset_to = 0;
+ p_to = p_to->next;
+ }
+ LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+ if (offset_from >= p_from->len) {
+ /* on to next p_from (if any) */
+ offset_from = 0;
+ p_from = p_from->next;
+ }
+
+ if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_from->next == NULL), return ERR_VAL;);
+ }
+ if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_to->next == NULL), return ERR_VAL;);
+ }
+ } while (p_from);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+ return ERR_OK;
+}
+
+/**
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+ struct pbuf *p;
+ u16_t left;
+ u16_t buf_copy_len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+ left = 0;
+
+ if((buf == NULL) || (dataptr == NULL)) {
+ return 0;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; len != 0 && p != NULL; p = p->next) {
+ if ((offset != 0) && (offset >= p->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset -= p->len;
+ } else {
+ /* copy from this buffer. maybe only partially. */
+ buf_copy_len = p->len - offset;
+ if (buf_copy_len > len)
+ buf_copy_len = len;
+ /* copy the necessary parts of the buffer */
+ MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+ copied_total += buf_copy_len;
+ left += buf_copy_len;
+ len -= buf_copy_len;
+ offset = 0;
+ }
+ }
+ return copied_total;
+}
+
+/**
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+ struct pbuf *p;
+ u16_t buf_copy_len;
+ u16_t total_copy_len = len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
+
+ if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+ return ERR_ARG;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; total_copy_len != 0; p = p->next) {
+ LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+ buf_copy_len = total_copy_len;
+ if (buf_copy_len > p->len) {
+ /* this pbuf cannot hold all remaining data */
+ buf_copy_len = p->len;
+ }
+ /* copy the necessary parts of the buffer */
+ MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+ total_copy_len -= buf_copy_len;
+ copied_total += buf_copy_len;
+ }
+ LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+ return ERR_OK;
+}
+
+/**
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ * pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ * or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+ struct pbuf *q;
+ err_t err;
+ if (p->next == NULL) {
+ return p;
+ }
+ q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+ if (q == NULL) {
+ /* @todo: what do we do now? */
+ return p;
+ }
+ err = pbuf_copy(q, p);
+ LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+ pbuf_free(p);
+ return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ * within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum)
+{
+ u32_t acc;
+ u16_t copy_chksum;
+ char *dst_ptr;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+ LWIP_ASSERT("chksum != NULL", chksum != NULL);
+ LWIP_ASSERT("len != 0", len != 0);
+
+ if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+ return ERR_ARG;
+ }
+
+ dst_ptr = ((char*)p->payload) + start_offset;
+ copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+ if ((start_offset & 1) != 0) {
+ copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+ }
+ acc = *chksum;
+ acc += copy_chksum;
+ *chksum = FOLD_U32T(acc);
+ return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /** Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(struct pbuf* p, u16_t offset)
+{
+ u16_t copy_from = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= copy_from)) {
+ copy_from -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > copy_from)) {
+ return ((u8_t*)q->payload)[copy_from];
+ }
+ return 0;
+}
+
+/** Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at wich to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ * (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+ u16_t start = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= start)) {
+ start -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > start)) {
+ u16_t i;
+ for(i = 0; i < n; i++) {
+ u8_t a = pbuf_get_at(q, start + i);
+ u8_t b = ((u8_t*)s2)[i];
+ if (a != b) {
+ return i+1;
+ }
+ }
+ return 0;
+ }
+ return 0xffff;
+}
+
+/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+ u16_t i;
+ u16_t max = p->tot_len - mem_len;
+ if (p->tot_len >= mem_len + start_offset) {
+ for(i = start_offset; i <= max; ) {
+ u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+ if (plus == 0) {
+ return i;
+ } else {
+ i += plus;
+ }
+ }
+ }
+ return 0xFFFF;
+}
+
+/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(struct pbuf* p, const char* substr)
+{
+ size_t substr_len;
+ if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+ return 0xFFFF;
+ }
+ substr_len = strlen(substr);
+ if (substr_len >= 0xFFFF) {
+ return 0xFFFF;
+ }
+ return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
diff --git a/core/lwip/src/core/raw.c b/core/lwip/src/core/raw.c
new file mode 100644
index 00000000..9fcb1003
--- /dev/null
+++ b/core/lwip/src/core/raw.c
@@ -0,0 +1,354 @@
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include <string.h>
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ * callback function. The caller MAY NOT not reference the
+ * packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ * caller).
+ *
+ */
+u8_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+ struct raw_pcb *pcb, *prev;
+ struct ip_hdr *iphdr;
+ s16_t proto;
+ u8_t eaten = 0;
+
+ LWIP_UNUSED_ARG(inp);
+
+ iphdr = (struct ip_hdr *)p->payload;
+ proto = IPH_PROTO(iphdr);
+
+ prev = NULL;
+ pcb = raw_pcbs;
+ /* loop through all raw pcbs until the packet is eaten by one */
+ /* this allows multiple pcbs to match against the packet by design */
+ while ((eaten == 0) && (pcb != NULL)) {
+ if ((pcb->protocol == proto) &&
+ (ip_addr_isany(&pcb->local_ip) ||
+ ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest))) {
+#if IP_SOF_BROADCAST_RECV
+ /* broadcast filter? */
+ if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(&current_iphdr_dest, inp))
+#endif /* IP_SOF_BROADCAST_RECV */
+ {
+ /* receive callback function available? */
+ if (pcb->recv != NULL) {
+ /* the receive callback function did not eat the packet? */
+ if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) {
+ /* receive function ate the packet */
+ p = NULL;
+ eaten = 1;
+ if (prev != NULL) {
+ /* move the pcb to the front of raw_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ }
+ }
+ /* no receive callback function was set for this raw PCB */
+ }
+ /* drop the packet */
+ }
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ return eaten;
+}
+
+/**
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t
+raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ip_addr_set(&pcb->local_ip, ipaddr);
+ return ERR_OK;
+}
+
+/**
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t
+raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ return ERR_OK;
+}
+
+
+/**
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ * packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ * against further PCBs and/or forwarded to another protocol layers.
+ *
+ * @return non-zero if the packet was free()d, zero if the packet remains
+ * available for others.
+ */
+void
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Send the raw IP packet to the given address. Note that actually you cannot
+ * modify the IP headers (this is inconsistent with the receive callback where
+ * you actually get the IP headers), you can only specify the IP payload here.
+ * It requires some more changes in lwIP. (there will be a raw_send() function
+ * then.)
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ err_t err;
+ struct netif *netif;
+ ip_addr_t *src_ip;
+ struct pbuf *q; /* q will be sent down the stack */
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+ /* not enough space to add an IP header to first pbuf in given p chain? */
+ if (pbuf_header(p, IP_HLEN)) {
+ /* allocate header in new pbuf */
+ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p */
+ pbuf_chain(q, p);
+ }
+ /* { first pbuf q points to header pbuf } */
+ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* first pbuf q equals given pbuf */
+ q = p;
+ if(pbuf_header(q, -IP_HLEN)) {
+ LWIP_ASSERT("Can't restore header we just removed!", 0);
+ return ERR_MEM;
+ }
+ }
+
+ if ((netif = ip_route(ipaddr)) == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+
+#if IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_VAL;
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = &(netif->ip_addr);
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &(pcb->local_ip);
+ }
+
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ /* did we chain a header earlier? */
+ if (q != p) {
+ /* free the header */
+ pbuf_free(q);
+ }
+ return err;
+}
+
+/**
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+ return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void
+raw_remove(struct raw_pcb *pcb)
+{
+ struct raw_pcb *pcb2;
+ /* pcb to be removed is first in list? */
+ if (raw_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ raw_pcbs = raw_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in raw_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ }
+ }
+ }
+ memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new(u8_t proto)
+{
+ struct raw_pcb *pcb;
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+
+ pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+ /* could allocate RAW PCB? */
+ if (pcb != NULL) {
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct raw_pcb));
+ pcb->protocol = proto;
+ pcb->ttl = RAW_TTL;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ return pcb;
+}
+
+#endif /* LWIP_RAW */
diff --git a/core/lwip/src/core/snmp/asn1_dec.c b/core/lwip/src/core/snmp/asn1_dec.c
new file mode 100644
index 00000000..1d565820
--- /dev/null
+++ b/core/lwip/src/core/snmp/asn1_dec.c
@@ -0,0 +1,657 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) decoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Retrieves type field from incoming pbuf chain.
+ *
+ * @param p points to a pbuf holding an ASN1 coded type field
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
+ * @param type return ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *type = *msg_ptr;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes length field from incoming pbuf chain into host length.
+ *
+ * @param p points to a pbuf holding an ASN1 coded length
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
+ * @param octets_used returns number of octets used by the length code
+ * @param length return host order length, upto 64k
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (*msg_ptr < 0x80)
+ {
+ /* primitive definite length format */
+ *octets_used = 1;
+ *length = *msg_ptr;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x80)
+ {
+ /* constructed indefinite length format, termination with two zero octets */
+ u8_t zeros;
+ u8_t i;
+
+ *length = 0;
+ zeros = 0;
+ while (zeros != 2)
+ {
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ (*length) += 1;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (*msg_ptr == 0)
+ {
+ zeros++;
+ if (zeros == 2)
+ {
+ /* stop while (i > 0) */
+ i = 0;
+ }
+ }
+ else
+ {
+ zeros = 0;
+ }
+ }
+ }
+ *octets_used = 1;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x81)
+ {
+ /* constructed definite length format, one octet */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *length = *msg_ptr;
+ *octets_used = 2;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x82)
+ {
+ u8_t i;
+
+ /* constructed definite length format, two octets */
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *length |= *msg_ptr;
+ }
+ else
+ {
+ /* most significant length octet */
+ *length = (*msg_ptr) << 8;
+ }
+ }
+ *octets_used = 3;
+ return ERR_OK;
+ }
+ else
+ {
+ /* constructed definite length format 3..127 octets, this is too big (>64k) */
+ /** @todo: do we need to accept inefficient codings with many leading zero's? */
+ *octets_used = 1 + ((*msg_ptr) & 0x7f);
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 6))
+ {
+ /* start from zero */
+ *value = 0;
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, expecting zero sign bit! */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* positive */
+ if ((len > 1) && (*msg_ptr == 0))
+ {
+ /* skip leading "sign byte" octet 0x00 */
+ len--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ }
+ /* OR octets with value */
+ while (len > 1)
+ {
+ len--;
+ *value |= *msg_ptr;
+ *value <<= 8;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ *value |= *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
+#endif
+ u8_t sign;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 5))
+ {
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, start from -1 */
+ *value = -1;
+ sign = 1;
+ }
+ else
+ {
+ /* positive, start from 0 */
+ *value = 0;
+ sign = 0;
+ }
+ /* OR/AND octets with value */
+ while (len > 1)
+ {
+ len--;
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ *value <<= 8;
+ *lsb_ptr |= 255;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ *value <<= 8;
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ }
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded object identifier
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
+ * @param len length of the coded object identifier
+ * @param oid return object identifier struct
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+ s32_t *oid_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ oid->len = 0;
+ oid_ptr = &oid->id[0];
+ if (len > 0)
+ {
+ /* first compressed octet */
+ if (*msg_ptr == 0x2B)
+ {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 40)
+ {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = *msg_ptr;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 80)
+ {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 40;
+ oid_ptr++;
+ }
+ else
+ {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 80;
+ oid_ptr++;
+ }
+ oid->len = 2;
+ }
+ else
+ {
+ /* accepting zero length identifiers e.g. for
+ getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+ len--;
+ if (len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
+ {
+ /* sub-identifier uses multiple octets */
+ if (*msg_ptr & 0x80)
+ {
+ s32_t sub_id = 0;
+
+ while ((*msg_ptr & 0x80) && (len > 1))
+ {
+ len--;
+ sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (!(*msg_ptr & 0x80) && (len > 0))
+ {
+ /* last octet sub-identifier */
+ len--;
+ sub_id = (sub_id << 7) + *msg_ptr;
+ *oid_ptr = sub_id;
+ }
+ }
+ else
+ {
+ /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
+ len--;
+ *oid_ptr = *msg_ptr;
+ }
+ if (len > 0)
+ {
+ /* remaining oid bytes available ... */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ oid_ptr++;
+ oid->len++;
+ }
+ if (len == 0)
+ {
+ /* len == 0, end of oid */
+ return ERR_OK;
+ }
+ else
+ {
+ /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
+ return ERR_ARG;
+ }
+
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param p points to a pbuf holding an ASN1 coded raw data
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param raw_len length of the raw return value
+ * @param raw return raw bytes
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ if (len > 0)
+ {
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if (raw_len >= len)
+ {
+ while (len > 1)
+ {
+ /* copy len - 1 octets */
+ len--;
+ *raw = *msg_ptr;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* copy last octet */
+ *raw = *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ /* raw_len < len, not enough dst space */
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* len == 0, empty string */
+ return ERR_OK;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/core/lwip/src/core/snmp/asn1_enc.c b/core/lwip/src/core/snmp/asn1_enc.c
new file mode 100644
index 00000000..64dfc5f6
--- /dev/null
+++ b/core/lwip/src/core/snmp/asn1_enc.c
@@ -0,0 +1,611 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+ if (length < 0x80U)
+ {
+ *octets_needed = 1;
+ }
+ else if (length < 0x100U)
+ {
+ *octets_needed = 2;
+ }
+ else
+ {
+ *octets_needed = 3;
+ }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+ if (value < 0x80UL)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000UL)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000UL)
+ {
+ *octets_needed = 3;
+ }
+ else if (value < 0x80000000UL)
+ {
+ *octets_needed = 4;
+ }
+ else
+ {
+ *octets_needed = 5;
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+ if (value < 0)
+ {
+ value = ~value;
+ }
+ if (value < 0x80L)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000L)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000L)
+ {
+ *octets_needed = 3;
+ }
+ else
+ {
+ *octets_needed = 4;
+ }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
+{
+ s32_t sub_id;
+ u8_t cnt;
+
+ cnt = 0;
+ if (ident_len > 1)
+ {
+ /* compressed prefix in one octet */
+ cnt++;
+ ident_len -= 2;
+ ident += 2;
+ }
+ while(ident_len > 0)
+ {
+ ident_len--;
+ sub_id = *ident;
+
+ sub_id >>= 7;
+ cnt++;
+ while(sub_id > 0)
+ {
+ sub_id >>= 7;
+ cnt++;
+ }
+ ident++;
+ }
+ *octets_needed = cnt;
+}
+
+/**
+ * Encodes ASN type field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param type input ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *msg_ptr = type;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes host order length field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode length into
+ * @param ofs points to the offset within the pbuf chain
+ * @param length is the host order length to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (length < 0x80)
+ {
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else if (length < 0x100)
+ {
+ *msg_ptr = 0x81;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else
+ {
+ u8_t i;
+
+ /* length >= 0x100 && length <= 0xFFFF */
+ *msg_ptr = 0x82;
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *msg_ptr = (u8_t)length;
+ }
+ else
+ {
+ /* most significant length octet */
+ *msg_ptr = (u8_t)(length >> 8);
+ }
+ }
+ return ERR_OK;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (octets_needed == 5)
+ {
+ /* not enough bits in 'value' add leading 0x00 */
+ octets_needed--;
+ *msg_ptr = 0x00;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode oid into
+ * @param ofs points to the offset within the pbuf chain
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (ident_len > 1)
+ {
+ if ((ident[0] == 1) && (ident[1] == 3))
+ {
+ /* compressed (most common) prefix .iso.org */
+ *msg_ptr = 0x2b;
+ }
+ else
+ {
+ /* calculate prefix */
+ *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ ident_len -= 2;
+ ident += 2;
+ }
+ else
+ {
+/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+ /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+ return ERR_ARG;
+ }
+ while (ident_len > 0)
+ {
+ s32_t sub_id;
+ u8_t shift, tail;
+
+ ident_len--;
+ sub_id = *ident;
+ tail = 0;
+ shift = 28;
+ while(shift > 0)
+ {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0))
+ {
+ tail = 1;
+ *msg_ptr = code | 0x80;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ shift -= 7;
+ }
+ *msg_ptr = (u8_t)sub_id & 0x7F;
+ if (ident_len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* proceed to next sub-identifier */
+ ident++;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode raw data into
+ * @param ofs points to the offset within the pbuf chain
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (raw_len > 1)
+ {
+ /* copy raw_len - 1 octets */
+ raw_len--;
+ *msg_ptr = *raw;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (raw_len > 0)
+ {
+ /* copy last or single octet */
+ *msg_ptr = *raw;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/core/lwip/src/core/snmp/mib2.c b/core/lwip/src/core/snmp/mib2.c
new file mode 100644
index 00000000..29decd30
--- /dev/null
+++ b/core/lwip/src/core/snmp/mib2.c
@@ -0,0 +1,4146 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ *
+ * @note the object identifiers for this MIB-2 and private MIB tree
+ * must be kept in sorted ascending order. This to ensure correct getnext operation.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/mem.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/udp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/sys.h"
+#include "netif/etharp.h"
+
+/**
+ * IANA assigned enterprise ID for lwIP is 26381
+ * @see http://www.iana.org/assignments/enterprise-numbers
+ *
+ * @note this enterprise ID is assigned to the lwIP project,
+ * all object identifiers living under this ID are assigned
+ * by the lwIP maintainers (contact Christiaan Simons)!
+ * @note don't change this define, use snmp_set_sysobjid()
+ *
+ * If you need to create your own private MIB you'll need
+ * to apply for your own enterprise ID with IANA:
+ * http://www.iana.org/numbers.html
+ */
+#define SNMP_ENTERPRISE_ID 26381
+#define SNMP_SYSOBJID_LEN 7
+#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID}
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+#ifndef SNMP_GET_SYSUPTIME
+#define SNMP_GET_SYSUPTIME(sysuptime)
+#endif
+
+static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void system_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t system_set_test(struct obj_def *od, u16_t len, void *value);
+static void system_set_value(struct obj_def *od, u16_t len, void *value);
+static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void interfaces_get_value(struct obj_def *od, u16_t len, void *value);
+static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ifentry_get_value(struct obj_def *od, u16_t len, void *value);
+#if !SNMP_SAFE_REQUESTS
+static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value);
+static void ifentry_set_value (struct obj_def *od, u16_t len, void *value);
+#endif /* SNMP_SAFE_REQUESTS */
+static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void atentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value);
+static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void icmp_get_value(struct obj_def *od, u16_t len, void *value);
+#if LWIP_TCP
+static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void tcp_get_value(struct obj_def *od, u16_t len, void *value);
+#ifdef THIS_SEEMS_UNUSED
+static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value);
+#endif
+#endif
+static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void udp_get_value(struct obj_def *od, u16_t len, void *value);
+static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void udpentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void snmp_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value);
+static void snmp_set_value(struct obj_def *od, u16_t len, void *value);
+
+
+/* snmp .1.3.6.1.2.1.11 */
+const mib_scalar_node snmp_scalar = {
+ &snmp_get_object_def,
+ &snmp_get_value,
+ &snmp_set_test,
+ &snmp_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t snmp_ids[28] = {
+ 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30
+};
+struct mib_node* const snmp_nodes[28] = {
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar
+};
+const struct mib_array_node snmp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 28,
+ snmp_ids,
+ snmp_nodes
+};
+
+/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
+/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
+/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
+
+/* udp .1.3.6.1.2.1.7 */
+/** index root node for udpTable */
+struct mib_list_rootnode udp_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t udpentry_ids[2] = { 1, 2 };
+struct mib_node* const udpentry_nodes[2] = {
+ (struct mib_node*)&udp_root, (struct mib_node*)&udp_root,
+};
+const struct mib_array_node udpentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ udpentry_ids,
+ udpentry_nodes
+};
+
+s32_t udptable_id = 1;
+struct mib_node* udptable_node = (struct mib_node*)&udpentry;
+struct mib_ram_array_node udptable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &udptable_id,
+ &udptable_node
+};
+
+const mib_scalar_node udp_scalar = {
+ &udp_get_object_def,
+ &udp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const udp_nodes[5] = {
+ (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
+ (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
+ (struct mib_node*)&udptable
+};
+const struct mib_array_node udp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ udp_ids,
+ udp_nodes
+};
+
+/* tcp .1.3.6.1.2.1.6 */
+#if LWIP_TCP
+/* only if the TCP protocol is available may implement this group */
+/** index root node for tcpConnTable */
+struct mib_list_rootnode tcpconntree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const tcpconnentry_nodes[5] = {
+ (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
+ (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
+ (struct mib_node*)&tcpconntree_root
+};
+const struct mib_array_node tcpconnentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ tcpconnentry_ids,
+ tcpconnentry_nodes
+};
+
+s32_t tcpconntable_id = 1;
+struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry;
+struct mib_ram_array_node tcpconntable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+/** @todo update maxlength when inserting / deleting from table
+ 0 when table is empty, 1 when more than one entry */
+ 0,
+ &tcpconntable_id,
+ &tcpconntable_node
+};
+
+const mib_scalar_node tcp_scalar = {
+ &tcp_get_object_def,
+ &tcp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+struct mib_node* const tcp_nodes[15] = {
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar
+};
+const struct mib_array_node tcp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 15,
+ tcp_ids,
+ tcp_nodes
+};
+#endif
+
+/* icmp .1.3.6.1.2.1.5 */
+const mib_scalar_node icmp_scalar = {
+ &icmp_get_object_def,
+ &icmp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
+struct mib_node* const icmp_nodes[26] = {
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar
+};
+const struct mib_array_node icmp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 26,
+ icmp_ids,
+ icmp_nodes
+};
+
+/** index root node for ipNetToMediaTable */
+struct mib_list_rootnode ipntomtree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 };
+struct mib_node* const ipntomentry_nodes[4] = {
+ (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root,
+ (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root
+};
+const struct mib_array_node ipntomentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 4,
+ ipntomentry_ids,
+ ipntomentry_nodes
+};
+
+s32_t ipntomtable_id = 1;
+struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry;
+struct mib_ram_array_node ipntomtable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &ipntomtable_id,
+ &ipntomtable_node
+};
+
+/** index root node for ipRouteTable */
+struct mib_list_rootnode iprtetree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
+struct mib_node* const iprteentry_nodes[13] = {
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root
+};
+const struct mib_array_node iprteentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 13,
+ iprteentry_ids,
+ iprteentry_nodes
+};
+
+s32_t iprtetable_id = 1;
+struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry;
+struct mib_ram_array_node iprtetable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &iprtetable_id,
+ &iprtetable_node
+};
+
+/** index root node for ipAddrTable */
+struct mib_list_rootnode ipaddrtree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const ipaddrentry_nodes[5] = {
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root
+};
+const struct mib_array_node ipaddrentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ ipaddrentry_ids,
+ ipaddrentry_nodes
+};
+
+s32_t ipaddrtable_id = 1;
+struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry;
+struct mib_ram_array_node ipaddrtable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &ipaddrtable_id,
+ &ipaddrtable_node
+};
+
+/* ip .1.3.6.1.2.1.4 */
+const mib_scalar_node ip_scalar = {
+ &ip_get_object_def,
+ &ip_get_value,
+ &ip_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
+struct mib_node* const ip_nodes[23] = {
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable,
+ (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable,
+ (struct mib_node*)&ip_scalar
+};
+const struct mib_array_node mib2_ip = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 23,
+ ip_ids,
+ ip_nodes
+};
+
+/** index root node for atTable */
+struct mib_list_rootnode arptree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t atentry_ids[3] = { 1, 2, 3 };
+struct mib_node* const atentry_nodes[3] = {
+ (struct mib_node*)&arptree_root,
+ (struct mib_node*)&arptree_root,
+ (struct mib_node*)&arptree_root
+};
+const struct mib_array_node atentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 3,
+ atentry_ids,
+ atentry_nodes
+};
+
+const s32_t attable_id = 1;
+struct mib_node* const attable_node = (struct mib_node*)&atentry;
+const struct mib_array_node attable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ &attable_id,
+ &attable_node
+};
+
+/* at .1.3.6.1.2.1.3 */
+s32_t at_id = 1;
+struct mib_node* mib2_at_node = (struct mib_node*)&attable;
+struct mib_ram_array_node at = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &at_id,
+ &mib2_at_node
+};
+
+/** index root node for ifTable */
+struct mib_list_rootnode iflist_root = {
+ &ifentry_get_object_def,
+ &ifentry_get_value,
+#if SNMP_SAFE_REQUESTS
+ &noleafs_set_test,
+ &noleafs_set_value,
+#else /* SNMP_SAFE_REQUESTS */
+ &ifentry_set_test,
+ &ifentry_set_value,
+#endif /* SNMP_SAFE_REQUESTS */
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
+struct mib_node* const ifentry_nodes[22] = {
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root
+};
+const struct mib_array_node ifentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 22,
+ ifentry_ids,
+ ifentry_nodes
+};
+
+s32_t iftable_id = 1;
+struct mib_node* iftable_node = (struct mib_node*)&ifentry;
+struct mib_ram_array_node iftable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &iftable_id,
+ &iftable_node
+};
+
+/* interfaces .1.3.6.1.2.1.2 */
+const mib_scalar_node interfaces_scalar = {
+ &interfaces_get_object_def,
+ &interfaces_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t interfaces_ids[2] = { 1, 2 };
+struct mib_node* const interfaces_nodes[2] = {
+ (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable
+};
+const struct mib_array_node interfaces = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ interfaces_ids,
+ interfaces_nodes
+};
+
+
+/* 0 1 2 3 4 5 6 */
+/* system .1.3.6.1.2.1.1 */
+const mib_scalar_node sys_tem_scalar = {
+ &system_get_object_def,
+ &system_get_value,
+ &system_set_test,
+ &system_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 };
+struct mib_node* const sys_tem_nodes[7] = {
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar
+};
+/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */
+const struct mib_array_node sys_tem = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 7,
+ sys_tem_ids,
+ sys_tem_nodes
+};
+
+/* mib-2 .1.3.6.1.2.1 */
+#if LWIP_TCP
+#define MIB2_GROUPS 8
+#else
+#define MIB2_GROUPS 7
+#endif
+const s32_t mib2_ids[MIB2_GROUPS] =
+{
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+#if LWIP_TCP
+ 6,
+#endif
+ 7,
+ 11
+};
+struct mib_node* const mib2_nodes[MIB2_GROUPS] = {
+ (struct mib_node*)&sys_tem,
+ (struct mib_node*)&interfaces,
+ (struct mib_node*)&at,
+ (struct mib_node*)&mib2_ip,
+ (struct mib_node*)&icmp,
+#if LWIP_TCP
+ (struct mib_node*)&tcp,
+#endif
+ (struct mib_node*)&udp,
+ (struct mib_node*)&snmp
+};
+
+const struct mib_array_node mib2 = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ MIB2_GROUPS,
+ mib2_ids,
+ mib2_nodes
+};
+
+/* mgmt .1.3.6.1.2 */
+const s32_t mgmt_ids[1] = { 1 };
+struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 };
+const struct mib_array_node mgmt = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ mgmt_ids,
+ mgmt_nodes
+};
+
+/* internet .1.3.6.1 */
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+s32_t internet_ids[2] = { 2, 4 };
+struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private };
+const struct mib_array_node internet = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ internet_ids,
+ internet_nodes
+};
+#else
+const s32_t internet_ids[1] = { 2 };
+struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt };
+const struct mib_array_node internet = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ internet_ids,
+ internet_nodes
+};
+#endif
+
+/** mib-2.system.sysObjectID */
+static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID};
+/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */
+static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}};
+/** mib-2.system.sysServices */
+static const s32_t sysservices = SNMP_SYSSERVICES;
+
+/** mib-2.system.sysDescr */
+static const u8_t sysdescr_len_default = 4;
+static const u8_t sysdescr_default[] = "lwIP";
+static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default;
+static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0];
+/** mib-2.system.sysContact */
+static const u8_t syscontact_len_default = 0;
+static const u8_t syscontact_default[] = "";
+static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default;
+static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0];
+/** mib-2.system.sysName */
+static const u8_t sysname_len_default = 8;
+static const u8_t sysname_default[] = "FQDN-unk";
+static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default;
+static u8_t* sysname_ptr = (u8_t*)&sysname_default[0];
+/** mib-2.system.sysLocation */
+static const u8_t syslocation_len_default = 0;
+static const u8_t syslocation_default[] = "";
+static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default;
+static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0];
+/** mib-2.snmp.snmpEnableAuthenTraps */
+static const u8_t snmpenableauthentraps_default = 2; /* disabled */
+static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default;
+
+/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */
+static const struct snmp_obj_id ifspecific = {2, {0, 0}};
+/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */
+static const struct snmp_obj_id iprouteinfo = {2, {0, 0}};
+
+
+
+/* mib-2.system counter(s) */
+static u32_t sysuptime = 0;
+
+/* mib-2.ip counter(s) */
+static u32_t ipinreceives = 0,
+ ipinhdrerrors = 0,
+ ipinaddrerrors = 0,
+ ipforwdatagrams = 0,
+ ipinunknownprotos = 0,
+ ipindiscards = 0,
+ ipindelivers = 0,
+ ipoutrequests = 0,
+ ipoutdiscards = 0,
+ ipoutnoroutes = 0,
+ ipreasmreqds = 0,
+ ipreasmoks = 0,
+ ipreasmfails = 0,
+ ipfragoks = 0,
+ ipfragfails = 0,
+ ipfragcreates = 0,
+ iproutingdiscards = 0;
+/* mib-2.icmp counter(s) */
+static u32_t icmpinmsgs = 0,
+ icmpinerrors = 0,
+ icmpindestunreachs = 0,
+ icmpintimeexcds = 0,
+ icmpinparmprobs = 0,
+ icmpinsrcquenchs = 0,
+ icmpinredirects = 0,
+ icmpinechos = 0,
+ icmpinechoreps = 0,
+ icmpintimestamps = 0,
+ icmpintimestampreps = 0,
+ icmpinaddrmasks = 0,
+ icmpinaddrmaskreps = 0,
+ icmpoutmsgs = 0,
+ icmpouterrors = 0,
+ icmpoutdestunreachs = 0,
+ icmpouttimeexcds = 0,
+ icmpoutparmprobs = 0,
+ icmpoutsrcquenchs = 0,
+ icmpoutredirects = 0,
+ icmpoutechos = 0,
+ icmpoutechoreps = 0,
+ icmpouttimestamps = 0,
+ icmpouttimestampreps = 0,
+ icmpoutaddrmasks = 0,
+ icmpoutaddrmaskreps = 0;
+/* mib-2.tcp counter(s) */
+static u32_t tcpactiveopens = 0,
+ tcppassiveopens = 0,
+ tcpattemptfails = 0,
+ tcpestabresets = 0,
+ tcpinsegs = 0,
+ tcpoutsegs = 0,
+ tcpretranssegs = 0,
+ tcpinerrs = 0,
+ tcpoutrsts = 0;
+/* mib-2.udp counter(s) */
+static u32_t udpindatagrams = 0,
+ udpnoports = 0,
+ udpinerrors = 0,
+ udpoutdatagrams = 0;
+/* mib-2.snmp counter(s) */
+static u32_t snmpinpkts = 0,
+ snmpoutpkts = 0,
+ snmpinbadversions = 0,
+ snmpinbadcommunitynames = 0,
+ snmpinbadcommunityuses = 0,
+ snmpinasnparseerrs = 0,
+ snmpintoobigs = 0,
+ snmpinnosuchnames = 0,
+ snmpinbadvalues = 0,
+ snmpinreadonlys = 0,
+ snmpingenerrs = 0,
+ snmpintotalreqvars = 0,
+ snmpintotalsetvars = 0,
+ snmpingetrequests = 0,
+ snmpingetnexts = 0,
+ snmpinsetrequests = 0,
+ snmpingetresponses = 0,
+ snmpintraps = 0,
+ snmpouttoobigs = 0,
+ snmpoutnosuchnames = 0,
+ snmpoutbadvalues = 0,
+ snmpoutgenerrs = 0,
+ snmpoutgetrequests = 0,
+ snmpoutgetnexts = 0,
+ snmpoutsetrequests = 0,
+ snmpoutgetresponses = 0,
+ snmpouttraps = 0;
+
+
+
+/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */
+/**
+ * Copy octet string.
+ *
+ * @param dst points to destination
+ * @param src points to source
+ * @param n number of octets to copy.
+ */
+static void ocstrncpy(u8_t *dst, u8_t *src, u16_t n)
+{
+ u16_t i = n;
+ while (i > 0) {
+ i--;
+ *dst++ = *src++;
+ }
+}
+
+/**
+ * Copy object identifier (s32_t) array.
+ *
+ * @param dst points to destination
+ * @param src points to source
+ * @param n number of sub identifiers to copy.
+ */
+void objectidncpy(s32_t *dst, s32_t *src, u8_t n)
+{
+ u8_t i = n;
+ while(i > 0) {
+ i--;
+ *dst++ = *src++;
+ }
+}
+
+/**
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void snmp_set_sysdesr(u8_t *str, u8_t *len)
+{
+ if (str != NULL)
+ {
+ sysdescr_ptr = str;
+ sysdescr_len_ptr = len;
+ }
+}
+
+void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid)
+{
+ *oid = &sysobjid;
+}
+
+/**
+ * Initializes sysObjectID value.
+ *
+ * @param oid points to stuct snmp_obj_id to copy
+ */
+void snmp_set_sysobjid(struct snmp_obj_id *oid)
+{
+ sysobjid = *oid;
+}
+
+/**
+ * Must be called at regular 10 msec interval from a timer interrupt
+ * or signal handler depending on your runtime environment.
+ */
+void snmp_inc_sysuptime(void)
+{
+ sysuptime++;
+}
+
+void snmp_add_sysuptime(u32_t value)
+{
+ sysuptime+=value;
+}
+
+void snmp_get_sysuptime(u32_t *value)
+{
+ SNMP_GET_SYSUPTIME(sysuptime);
+ *value = sysuptime;
+}
+
+/**
+ * Initializes sysContact pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ syscontact_ptr = ocstr;
+ syscontact_len_ptr = ocstrlen;
+ }
+}
+
+/**
+ * Initializes sysName pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ sysname_ptr = ocstr;
+ sysname_len_ptr = ocstrlen;
+ }
+}
+
+/**
+ * Initializes sysLocation pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ syslocation_ptr = ocstr;
+ syslocation_len_ptr = ocstrlen;
+ }
+}
+
+
+void snmp_add_ifinoctets(struct netif *ni, u32_t value)
+{
+ ni->ifinoctets += value;
+}
+
+void snmp_inc_ifinucastpkts(struct netif *ni)
+{
+ (ni->ifinucastpkts)++;
+}
+
+void snmp_inc_ifinnucastpkts(struct netif *ni)
+{
+ (ni->ifinnucastpkts)++;
+}
+
+void snmp_inc_ifindiscards(struct netif *ni)
+{
+ (ni->ifindiscards)++;
+}
+
+void snmp_add_ifoutoctets(struct netif *ni, u32_t value)
+{
+ ni->ifoutoctets += value;
+}
+
+void snmp_inc_ifoutucastpkts(struct netif *ni)
+{
+ (ni->ifoutucastpkts)++;
+}
+
+void snmp_inc_ifoutnucastpkts(struct netif *ni)
+{
+ (ni->ifoutnucastpkts)++;
+}
+
+void snmp_inc_ifoutdiscards(struct netif *ni)
+{
+ (ni->ifoutdiscards)++;
+}
+
+void snmp_inc_iflist(void)
+{
+ struct mib_list_node *if_node = NULL;
+
+ snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node);
+ /* enable getnext traversal on filled table */
+ iftable.maxlength = 1;
+}
+
+void snmp_dec_iflist(void)
+{
+ snmp_mib_node_delete(&iflist_root, iflist_root.tail);
+ /* disable getnext traversal on empty table */
+ if(iflist_root.count == 0) iftable.maxlength = 0;
+}
+
+/**
+ * Inserts ARP table indexes (.xIfIndex.xNetAddress)
+ * into arp table index trees (both atTable and ipNetToMediaTable).
+ */
+void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip)
+{
+ struct mib_list_rootnode *at_rn;
+ struct mib_list_node *at_node;
+ s32_t arpidx[5];
+ u8_t level, tree;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_netiftoifindex(ni, &arpidx[0]);
+ snmp_iptooid(ip, &arpidx[1]);
+
+ for (tree = 0; tree < 2; tree++)
+ {
+ if (tree == 0)
+ {
+ at_rn = &arptree_root;
+ }
+ else
+ {
+ at_rn = &ipntomtree_root;
+ }
+ for (level = 0; level < 5; level++)
+ {
+ at_node = NULL;
+ snmp_mib_node_insert(at_rn, arpidx[level], &at_node);
+ if ((level != 4) && (at_node != NULL))
+ {
+ if (at_node->nptr == NULL)
+ {
+ at_rn = snmp_mib_lrn_alloc();
+ at_node->nptr = (struct mib_node*)at_rn;
+ if (at_rn != NULL)
+ {
+ if (level == 3)
+ {
+ if (tree == 0)
+ {
+ at_rn->get_object_def = atentry_get_object_def;
+ at_rn->get_value = atentry_get_value;
+ }
+ else
+ {
+ at_rn->get_object_def = ip_ntomentry_get_object_def;
+ at_rn->get_value = ip_ntomentry_get_value;
+ }
+ at_rn->set_test = noleafs_set_test;
+ at_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* at_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ at_rn = (struct mib_list_rootnode*)at_node->nptr;
+ }
+ }
+ }
+ }
+ /* enable getnext traversal on filled tables */
+ at.maxlength = 1;
+ ipntomtable.maxlength = 1;
+}
+
+/**
+ * Removes ARP table indexes (.xIfIndex.xNetAddress)
+ * from arp table index trees.
+ */
+void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip)
+{
+ struct mib_list_rootnode *at_rn, *next, *del_rn[5];
+ struct mib_list_node *at_n, *del_n[5];
+ s32_t arpidx[5];
+ u8_t fc, tree, level, del_cnt;
+
+ snmp_netiftoifindex(ni, &arpidx[0]);
+ snmp_iptooid(ip, &arpidx[1]);
+
+ for (tree = 0; tree < 2; tree++)
+ {
+ /* mark nodes for deletion */
+ if (tree == 0)
+ {
+ at_rn = &arptree_root;
+ }
+ else
+ {
+ at_rn = &ipntomtree_root;
+ }
+ level = 0;
+ del_cnt = 0;
+ while ((level < 5) && (at_rn != NULL))
+ {
+ fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n);
+ if (fc == 0)
+ {
+ /* arpidx[level] does not exist */
+ del_cnt = 0;
+ at_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = at_rn;
+ del_n[del_cnt] = at_n;
+ del_cnt++;
+ at_rn = (struct mib_list_rootnode*)(at_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ at_rn = (struct mib_list_rootnode*)(at_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ at_rn = del_rn[del_cnt];
+ at_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(at_rn, at_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty tables */
+ if(arptree_root.count == 0) at.maxlength = 0;
+ if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0;
+}
+
+void snmp_inc_ipinreceives(void)
+{
+ ipinreceives++;
+}
+
+void snmp_inc_ipinhdrerrors(void)
+{
+ ipinhdrerrors++;
+}
+
+void snmp_inc_ipinaddrerrors(void)
+{
+ ipinaddrerrors++;
+}
+
+void snmp_inc_ipforwdatagrams(void)
+{
+ ipforwdatagrams++;
+}
+
+void snmp_inc_ipinunknownprotos(void)
+{
+ ipinunknownprotos++;
+}
+
+void snmp_inc_ipindiscards(void)
+{
+ ipindiscards++;
+}
+
+void snmp_inc_ipindelivers(void)
+{
+ ipindelivers++;
+}
+
+void snmp_inc_ipoutrequests(void)
+{
+ ipoutrequests++;
+}
+
+void snmp_inc_ipoutdiscards(void)
+{
+ ipoutdiscards++;
+}
+
+void snmp_inc_ipoutnoroutes(void)
+{
+ ipoutnoroutes++;
+}
+
+void snmp_inc_ipreasmreqds(void)
+{
+ ipreasmreqds++;
+}
+
+void snmp_inc_ipreasmoks(void)
+{
+ ipreasmoks++;
+}
+
+void snmp_inc_ipreasmfails(void)
+{
+ ipreasmfails++;
+}
+
+void snmp_inc_ipfragoks(void)
+{
+ ipfragoks++;
+}
+
+void snmp_inc_ipfragfails(void)
+{
+ ipfragfails++;
+}
+
+void snmp_inc_ipfragcreates(void)
+{
+ ipfragcreates++;
+}
+
+void snmp_inc_iproutingdiscards(void)
+{
+ iproutingdiscards++;
+}
+
+/**
+ * Inserts ipAddrTable indexes (.ipAdEntAddr)
+ * into index tree.
+ */
+void snmp_insert_ipaddridx_tree(struct netif *ni)
+{
+ struct mib_list_rootnode *ipa_rn;
+ struct mib_list_node *ipa_node;
+ s32_t ipaddridx[4];
+ u8_t level;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
+
+ level = 0;
+ ipa_rn = &ipaddrtree_root;
+ while (level < 4)
+ {
+ ipa_node = NULL;
+ snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node);
+ if ((level != 3) && (ipa_node != NULL))
+ {
+ if (ipa_node->nptr == NULL)
+ {
+ ipa_rn = snmp_mib_lrn_alloc();
+ ipa_node->nptr = (struct mib_node*)ipa_rn;
+ if (ipa_rn != NULL)
+ {
+ if (level == 2)
+ {
+ ipa_rn->get_object_def = ip_addrentry_get_object_def;
+ ipa_rn->get_value = ip_addrentry_get_value;
+ ipa_rn->set_test = noleafs_set_test;
+ ipa_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* ipa_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr;
+ }
+ }
+ level++;
+ }
+ /* enable getnext traversal on filled table */
+ ipaddrtable.maxlength = 1;
+}
+
+/**
+ * Removes ipAddrTable indexes (.ipAdEntAddr)
+ * from index tree.
+ */
+void snmp_delete_ipaddridx_tree(struct netif *ni)
+{
+ struct mib_list_rootnode *ipa_rn, *next, *del_rn[4];
+ struct mib_list_node *ipa_n, *del_n[4];
+ s32_t ipaddridx[4];
+ u8_t fc, level, del_cnt;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
+
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ ipa_rn = &ipaddrtree_root;
+ while ((level < 4) && (ipa_rn != NULL))
+ {
+ fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n);
+ if (fc == 0)
+ {
+ /* ipaddridx[level] does not exist */
+ del_cnt = 0;
+ ipa_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = ipa_rn;
+ del_n[del_cnt] = ipa_n;
+ del_cnt++;
+ ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ ipa_rn = del_rn[del_cnt];
+ ipa_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(ipa_rn, ipa_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0;
+}
+
+/**
+ * Inserts ipRouteTable indexes (.ipRouteDest)
+ * into index tree.
+ *
+ * @param dflt non-zero for the default rte, zero for network rte
+ * @param ni points to network interface for this rte
+ *
+ * @todo record sysuptime for _this_ route when it is installed
+ * (needed for ipRouteAge) in the netif.
+ */
+void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni)
+{
+ u8_t insert = 0;
+ ip_addr_t dst;
+
+ if (dflt != 0)
+ {
+ /* the default route 0.0.0.0 */
+ ip_addr_set_any(&dst);
+ insert = 1;
+ }
+ else
+ {
+ /* route to the network address */
+ ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
+ /* exclude 0.0.0.0 network (reserved for default rte) */
+ if (!ip_addr_isany(&dst)) {
+ insert = 1;
+ }
+ }
+ if (insert)
+ {
+ struct mib_list_rootnode *iprte_rn;
+ struct mib_list_node *iprte_node;
+ s32_t iprteidx[4];
+ u8_t level;
+
+ snmp_iptooid(&dst, &iprteidx[0]);
+ level = 0;
+ iprte_rn = &iprtetree_root;
+ while (level < 4)
+ {
+ iprte_node = NULL;
+ snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node);
+ if ((level != 3) && (iprte_node != NULL))
+ {
+ if (iprte_node->nptr == NULL)
+ {
+ iprte_rn = snmp_mib_lrn_alloc();
+ iprte_node->nptr = (struct mib_node*)iprte_rn;
+ if (iprte_rn != NULL)
+ {
+ if (level == 2)
+ {
+ iprte_rn->get_object_def = ip_rteentry_get_object_def;
+ iprte_rn->get_value = ip_rteentry_get_value;
+ iprte_rn->set_test = noleafs_set_test;
+ iprte_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* iprte_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr;
+ }
+ }
+ level++;
+ }
+ }
+ /* enable getnext traversal on filled table */
+ iprtetable.maxlength = 1;
+}
+
+/**
+ * Removes ipRouteTable indexes (.ipRouteDest)
+ * from index tree.
+ *
+ * @param dflt non-zero for the default rte, zero for network rte
+ * @param ni points to network interface for this rte or NULL
+ * for default route to be removed.
+ */
+void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni)
+{
+ u8_t del = 0;
+ ip_addr_t dst;
+
+ if (dflt != 0)
+ {
+ /* the default route 0.0.0.0 */
+ ip_addr_set_any(&dst);
+ del = 1;
+ }
+ else
+ {
+ /* route to the network address */
+ ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
+ /* exclude 0.0.0.0 network (reserved for default rte) */
+ if (!ip_addr_isany(&dst)) {
+ del = 1;
+ }
+ }
+ if (del)
+ {
+ struct mib_list_rootnode *iprte_rn, *next, *del_rn[4];
+ struct mib_list_node *iprte_n, *del_n[4];
+ s32_t iprteidx[4];
+ u8_t fc, level, del_cnt;
+
+ snmp_iptooid(&dst, &iprteidx[0]);
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ iprte_rn = &iprtetree_root;
+ while ((level < 4) && (iprte_rn != NULL))
+ {
+ fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n);
+ if (fc == 0)
+ {
+ /* iprteidx[level] does not exist */
+ del_cnt = 0;
+ iprte_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = iprte_rn;
+ del_n[del_cnt] = iprte_n;
+ del_cnt++;
+ iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ iprte_rn = del_rn[del_cnt];
+ iprte_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(iprte_rn, iprte_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (iprtetree_root.count == 0) iprtetable.maxlength = 0;
+}
+
+
+void snmp_inc_icmpinmsgs(void)
+{
+ icmpinmsgs++;
+}
+
+void snmp_inc_icmpinerrors(void)
+{
+ icmpinerrors++;
+}
+
+void snmp_inc_icmpindestunreachs(void)
+{
+ icmpindestunreachs++;
+}
+
+void snmp_inc_icmpintimeexcds(void)
+{
+ icmpintimeexcds++;
+}
+
+void snmp_inc_icmpinparmprobs(void)
+{
+ icmpinparmprobs++;
+}
+
+void snmp_inc_icmpinsrcquenchs(void)
+{
+ icmpinsrcquenchs++;
+}
+
+void snmp_inc_icmpinredirects(void)
+{
+ icmpinredirects++;
+}
+
+void snmp_inc_icmpinechos(void)
+{
+ icmpinechos++;
+}
+
+void snmp_inc_icmpinechoreps(void)
+{
+ icmpinechoreps++;
+}
+
+void snmp_inc_icmpintimestamps(void)
+{
+ icmpintimestamps++;
+}
+
+void snmp_inc_icmpintimestampreps(void)
+{
+ icmpintimestampreps++;
+}
+
+void snmp_inc_icmpinaddrmasks(void)
+{
+ icmpinaddrmasks++;
+}
+
+void snmp_inc_icmpinaddrmaskreps(void)
+{
+ icmpinaddrmaskreps++;
+}
+
+void snmp_inc_icmpoutmsgs(void)
+{
+ icmpoutmsgs++;
+}
+
+void snmp_inc_icmpouterrors(void)
+{
+ icmpouterrors++;
+}
+
+void snmp_inc_icmpoutdestunreachs(void)
+{
+ icmpoutdestunreachs++;
+}
+
+void snmp_inc_icmpouttimeexcds(void)
+{
+ icmpouttimeexcds++;
+}
+
+void snmp_inc_icmpoutparmprobs(void)
+{
+ icmpoutparmprobs++;
+}
+
+void snmp_inc_icmpoutsrcquenchs(void)
+{
+ icmpoutsrcquenchs++;
+}
+
+void snmp_inc_icmpoutredirects(void)
+{
+ icmpoutredirects++;
+}
+
+void snmp_inc_icmpoutechos(void)
+{
+ icmpoutechos++;
+}
+
+void snmp_inc_icmpoutechoreps(void)
+{
+ icmpoutechoreps++;
+}
+
+void snmp_inc_icmpouttimestamps(void)
+{
+ icmpouttimestamps++;
+}
+
+void snmp_inc_icmpouttimestampreps(void)
+{
+ icmpouttimestampreps++;
+}
+
+void snmp_inc_icmpoutaddrmasks(void)
+{
+ icmpoutaddrmasks++;
+}
+
+void snmp_inc_icmpoutaddrmaskreps(void)
+{
+ icmpoutaddrmaskreps++;
+}
+
+void snmp_inc_tcpactiveopens(void)
+{
+ tcpactiveopens++;
+}
+
+void snmp_inc_tcppassiveopens(void)
+{
+ tcppassiveopens++;
+}
+
+void snmp_inc_tcpattemptfails(void)
+{
+ tcpattemptfails++;
+}
+
+void snmp_inc_tcpestabresets(void)
+{
+ tcpestabresets++;
+}
+
+void snmp_inc_tcpinsegs(void)
+{
+ tcpinsegs++;
+}
+
+void snmp_inc_tcpoutsegs(void)
+{
+ tcpoutsegs++;
+}
+
+void snmp_inc_tcpretranssegs(void)
+{
+ tcpretranssegs++;
+}
+
+void snmp_inc_tcpinerrs(void)
+{
+ tcpinerrs++;
+}
+
+void snmp_inc_tcpoutrsts(void)
+{
+ tcpoutrsts++;
+}
+
+void snmp_inc_udpindatagrams(void)
+{
+ udpindatagrams++;
+}
+
+void snmp_inc_udpnoports(void)
+{
+ udpnoports++;
+}
+
+void snmp_inc_udpinerrors(void)
+{
+ udpinerrors++;
+}
+
+void snmp_inc_udpoutdatagrams(void)
+{
+ udpoutdatagrams++;
+}
+
+/**
+ * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * into index tree.
+ */
+void snmp_insert_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct mib_list_rootnode *udp_rn;
+ struct mib_list_node *udp_node;
+ s32_t udpidx[5];
+ u8_t level;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(&pcb->local_ip, &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ udp_rn = &udp_root;
+ for (level = 0; level < 5; level++)
+ {
+ udp_node = NULL;
+ snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node);
+ if ((level != 4) && (udp_node != NULL))
+ {
+ if (udp_node->nptr == NULL)
+ {
+ udp_rn = snmp_mib_lrn_alloc();
+ udp_node->nptr = (struct mib_node*)udp_rn;
+ if (udp_rn != NULL)
+ {
+ if (level == 3)
+ {
+ udp_rn->get_object_def = udpentry_get_object_def;
+ udp_rn->get_value = udpentry_get_value;
+ udp_rn->set_test = noleafs_set_test;
+ udp_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* udp_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ udp_rn = (struct mib_list_rootnode*)udp_node->nptr;
+ }
+ }
+ }
+ udptable.maxlength = 1;
+}
+
+/**
+ * Removes udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * from index tree.
+ */
+void snmp_delete_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct udp_pcb *npcb;
+ struct mib_list_rootnode *udp_rn, *next, *del_rn[5];
+ struct mib_list_node *udp_n, *del_n[5];
+ s32_t udpidx[5];
+ u8_t bindings, fc, level, del_cnt;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(&pcb->local_ip, &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ /* count PCBs for a given binding
+ (e.g. when reusing ports or for temp output PCBs) */
+ bindings = 0;
+ npcb = udp_pcbs;
+ while ((npcb != NULL))
+ {
+ if (ip_addr_cmp(&npcb->local_ip, &pcb->local_ip) &&
+ (npcb->local_port == udpidx[4]))
+ {
+ bindings++;
+ }
+ npcb = npcb->next;
+ }
+ if (bindings == 1)
+ {
+ /* selectively remove */
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ udp_rn = &udp_root;
+ while ((level < 5) && (udp_rn != NULL))
+ {
+ fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n);
+ if (fc == 0)
+ {
+ /* udpidx[level] does not exist */
+ del_cnt = 0;
+ udp_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = udp_rn;
+ del_n[del_cnt] = udp_n;
+ del_cnt++;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ udp_rn = del_rn[del_cnt];
+ udp_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(udp_rn, udp_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (udp_root.count == 0) udptable.maxlength = 0;
+}
+
+
+void snmp_inc_snmpinpkts(void)
+{
+ snmpinpkts++;
+}
+
+void snmp_inc_snmpoutpkts(void)
+{
+ snmpoutpkts++;
+}
+
+void snmp_inc_snmpinbadversions(void)
+{
+ snmpinbadversions++;
+}
+
+void snmp_inc_snmpinbadcommunitynames(void)
+{
+ snmpinbadcommunitynames++;
+}
+
+void snmp_inc_snmpinbadcommunityuses(void)
+{
+ snmpinbadcommunityuses++;
+}
+
+void snmp_inc_snmpinasnparseerrs(void)
+{
+ snmpinasnparseerrs++;
+}
+
+void snmp_inc_snmpintoobigs(void)
+{
+ snmpintoobigs++;
+}
+
+void snmp_inc_snmpinnosuchnames(void)
+{
+ snmpinnosuchnames++;
+}
+
+void snmp_inc_snmpinbadvalues(void)
+{
+ snmpinbadvalues++;
+}
+
+void snmp_inc_snmpinreadonlys(void)
+{
+ snmpinreadonlys++;
+}
+
+void snmp_inc_snmpingenerrs(void)
+{
+ snmpingenerrs++;
+}
+
+void snmp_add_snmpintotalreqvars(u8_t value)
+{
+ snmpintotalreqvars += value;
+}
+
+void snmp_add_snmpintotalsetvars(u8_t value)
+{
+ snmpintotalsetvars += value;
+}
+
+void snmp_inc_snmpingetrequests(void)
+{
+ snmpingetrequests++;
+}
+
+void snmp_inc_snmpingetnexts(void)
+{
+ snmpingetnexts++;
+}
+
+void snmp_inc_snmpinsetrequests(void)
+{
+ snmpinsetrequests++;
+}
+
+void snmp_inc_snmpingetresponses(void)
+{
+ snmpingetresponses++;
+}
+
+void snmp_inc_snmpintraps(void)
+{
+ snmpintraps++;
+}
+
+void snmp_inc_snmpouttoobigs(void)
+{
+ snmpouttoobigs++;
+}
+
+void snmp_inc_snmpoutnosuchnames(void)
+{
+ snmpoutnosuchnames++;
+}
+
+void snmp_inc_snmpoutbadvalues(void)
+{
+ snmpoutbadvalues++;
+}
+
+void snmp_inc_snmpoutgenerrs(void)
+{
+ snmpoutgenerrs++;
+}
+
+void snmp_inc_snmpoutgetrequests(void)
+{
+ snmpoutgetrequests++;
+}
+
+void snmp_inc_snmpoutgetnexts(void)
+{
+ snmpoutgetnexts++;
+}
+
+void snmp_inc_snmpoutsetrequests(void)
+{
+ snmpoutsetrequests++;
+}
+
+void snmp_inc_snmpoutgetresponses(void)
+{
+ snmpoutgetresponses++;
+}
+
+void snmp_inc_snmpouttraps(void)
+{
+ snmpouttraps++;
+}
+
+void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid)
+{
+ *oid = &snmpgrp_id;
+}
+
+void snmp_set_snmpenableauthentraps(u8_t *value)
+{
+ if (value != NULL)
+ {
+ snmpenableauthentraps_ptr = value;
+ }
+}
+
+void snmp_get_snmpenableauthentraps(u8_t *value)
+{
+ *value = *snmpenableauthentraps_ptr;
+}
+
+void
+noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ LWIP_UNUSED_ARG(ident_len);
+ LWIP_UNUSED_ARG(ident);
+ od->instance = MIB_OBJECT_NONE;
+}
+
+void
+noleafs_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+}
+
+u8_t
+noleafs_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+ /* can't set */
+ return 0;
+}
+
+void
+noleafs_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+}
+
+
+/**
+ * Returns systems object definitions.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param od points to object definition.
+ */
+static void
+system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* sysDescr */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *sysdescr_len_ptr;
+ break;
+ case 2: /* sysObjectID */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = sysobjid.len * sizeof(s32_t);
+ break;
+ case 3: /* sysUpTime */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 4: /* sysContact */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *syscontact_len_ptr;
+ break;
+ case 5: /* sysName */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *sysname_len_ptr;
+ break;
+ case 6: /* sysLocation */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *syslocation_len_ptr;
+ break;
+ case 7: /* sysServices */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns system object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+system_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* sysDescr */
+ ocstrncpy((u8_t*)value, sysdescr_ptr, len);
+ break;
+ case 2: /* sysObjectID */
+ objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ case 3: /* sysUpTime */
+ {
+ snmp_get_sysuptime((u32_t*)value);
+ }
+ break;
+ case 4: /* sysContact */
+ ocstrncpy((u8_t*)value, syscontact_ptr, len);
+ break;
+ case 5: /* sysName */
+ ocstrncpy((u8_t*)value, sysname_ptr, len);
+ break;
+ case 6: /* sysLocation */
+ ocstrncpy((u8_t*)value, syslocation_ptr, len);
+ break;
+ case 7: /* sysServices */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = sysservices;
+ }
+ break;
+ };
+}
+
+static u8_t
+system_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+
+ LWIP_UNUSED_ARG(value);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 4: /* sysContact */
+ if ((syscontact_ptr != syscontact_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ case 5: /* sysName */
+ if ((sysname_ptr != sysname_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ case 6: /* sysLocation */
+ if ((syslocation_ptr != syslocation_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ };
+ return set_ok;
+}
+
+static void
+system_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_ASSERT("invalid len", len <= 0xff);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 4: /* sysContact */
+ ocstrncpy(syscontact_ptr, (u8_t*)value, len);
+ *syscontact_len_ptr = (u8_t)len;
+ break;
+ case 5: /* sysName */
+ ocstrncpy(sysname_ptr, (u8_t*)value, len);
+ *sysname_len_ptr = (u8_t)len;
+ break;
+ case 6: /* sysLocation */
+ ocstrncpy(syslocation_ptr, (u8_t*)value, len);
+ *syslocation_len_ptr = (u8_t)len;
+ break;
+ };
+}
+
+/**
+ * Returns interfaces.ifnumber object definition.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.index
+ * @param od points to object definition.
+ */
+static void
+interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns interfaces.ifnumber object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+interfaces_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(len);
+ if (od->id_inst_ptr[0] == 1)
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = iflist_root.count;
+ }
+}
+
+/**
+ * Returns ifentry object definitions.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.index
+ * @param od points to object definition.
+ */
+static void
+ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* ifIndex */
+ case 3: /* ifType */
+ case 4: /* ifMtu */
+ case 8: /* ifOperStatus */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* ifDescr */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ /** @todo this should be some sort of sizeof(struct netif.name) */
+ od->v_len = 2;
+ break;
+ case 5: /* ifSpeed */
+ case 21: /* ifOutQLen */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 6: /* ifPhysAddress */
+ {
+ struct netif *netif;
+
+ snmp_ifindextonetif(ident[1], &netif);
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = netif->hwaddr_len;
+ }
+ break;
+ case 7: /* ifAdminStatus */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 9: /* ifLastChange */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 10: /* ifInOctets */
+ case 11: /* ifInUcastPkts */
+ case 12: /* ifInNUcastPkts */
+ case 13: /* ifInDiscarts */
+ case 14: /* ifInErrors */
+ case 15: /* ifInUnkownProtos */
+ case 16: /* ifOutOctets */
+ case 17: /* ifOutUcastPkts */
+ case 18: /* ifOutNUcastPkts */
+ case 19: /* ifOutDiscarts */
+ case 20: /* ifOutErrors */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 22: /* ifSpecific */
+ /** @note returning zeroDotZero (0.0) no media specific MIB support */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = ifspecific.len * sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns ifentry object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+ifentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id;
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ifIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* ifDescr */
+ ocstrncpy((u8_t*)value, (u8_t*)netif->name, len);
+ break;
+ case 3: /* ifType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = netif->link_type;
+ }
+ break;
+ case 4: /* ifMtu */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = netif->mtu;
+ }
+ break;
+ case 5: /* ifSpeed */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->link_speed;
+ }
+ break;
+ case 6: /* ifPhysAddress */
+ ocstrncpy((u8_t*)value, netif->hwaddr, len);
+ break;
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (netif_is_up(netif))
+ {
+ if (netif_is_link_up(netif))
+ {
+ *sint_ptr = 1; /* up */
+ }
+ else
+ {
+ *sint_ptr = 7; /* lowerLayerDown */
+ }
+ }
+ else
+ {
+ *sint_ptr = 2; /* down */
+ }
+ }
+ break;
+ case 8: /* ifOperStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (netif_is_up(netif))
+ {
+ *sint_ptr = 1;
+ }
+ else
+ {
+ *sint_ptr = 2;
+ }
+ }
+ break;
+ case 9: /* ifLastChange */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ts;
+ }
+ break;
+ case 10: /* ifInOctets */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinoctets;
+ }
+ break;
+ case 11: /* ifInUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinucastpkts;
+ }
+ break;
+ case 12: /* ifInNUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinnucastpkts;
+ }
+ break;
+ case 13: /* ifInDiscarts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifindiscards;
+ }
+ break;
+ case 14: /* ifInErrors */
+ case 15: /* ifInUnkownProtos */
+ /** @todo add these counters! */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 16: /* ifOutOctets */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutoctets;
+ }
+ break;
+ case 17: /* ifOutUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutucastpkts;
+ }
+ break;
+ case 18: /* ifOutNUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutnucastpkts;
+ }
+ break;
+ case 19: /* ifOutDiscarts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutdiscards;
+ }
+ break;
+ case 20: /* ifOutErrors */
+ /** @todo add this counter! */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 21: /* ifOutQLen */
+ /** @todo figure out if this must be 0 (no queue) or 1? */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 22: /* ifSpecific */
+ objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ };
+}
+
+#if !SNMP_SAFE_REQUESTS
+static u8_t
+ifentry_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id, set_ok;
+ LWIP_UNUSED_ARG(len);
+
+ set_ok = 0;
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == 1 || *sint_ptr == 2)
+ set_ok = 1;
+ }
+ break;
+ }
+ return set_ok;
+}
+
+static void
+ifentry_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id;
+ LWIP_UNUSED_ARG(len);
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == 1)
+ {
+ netif_set_up(netif);
+ }
+ else if (*sint_ptr == 2)
+ {
+ netif_set_down(netif);
+ }
+ }
+ break;
+ }
+}
+#endif /* SNMP_SAFE_REQUESTS */
+
+/**
+ * Returns atentry object definitions.
+ *
+ * @param ident_len the address length (6)
+ * @param ident points to objectname.atifindex.atnetaddress
+ * @param od points to object definition.
+ */
+static void
+atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* atIfIndex */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* atPhysAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = 6; /** @todo try to use netif::hwaddr_len */
+ break;
+ case 3: /* atNetAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+atentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+#if LWIP_ARP
+ u8_t id;
+ struct eth_addr* ethaddr_ret;
+ ip_addr_t* ipaddr_ret;
+#endif /* LWIP_ARP */
+ ip_addr_t ip;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ snmp_oidtoip(&od->id_inst_ptr[2], &ip);
+
+#if LWIP_ARP /** @todo implement a netif_find_addr */
+ if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* atIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* atPhysAddress */
+ {
+ struct eth_addr *dst = (struct eth_addr*)value;
+
+ *dst = *ethaddr_ret;
+ }
+ break;
+ case 3: /* atNetAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ *dst = *ipaddr_ret;
+ }
+ break;
+ }
+ }
+#endif /* LWIP_ARP */
+}
+
+static void
+ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+ case 2: /* ipDefaultTTL */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 3: /* ipInReceives */
+ case 4: /* ipInHdrErrors */
+ case 5: /* ipInAddrErrors */
+ case 6: /* ipForwDatagrams */
+ case 7: /* ipInUnknownProtos */
+ case 8: /* ipInDiscards */
+ case 9: /* ipInDelivers */
+ case 10: /* ipOutRequests */
+ case 11: /* ipOutDiscards */
+ case 12: /* ipOutNoRoutes */
+ case 14: /* ipReasmReqds */
+ case 15: /* ipReasmOKs */
+ case 16: /* ipReasmFails */
+ case 17: /* ipFragOKs */
+ case 18: /* ipFragFails */
+ case 19: /* ipFragCreates */
+ case 23: /* ipRoutingDiscards */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 13: /* ipReasmTimeout */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_FORWARD
+ /* forwarding */
+ *sint_ptr = 1;
+#else
+ /* not-forwarding */
+ *sint_ptr = 2;
+#endif
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = IP_DEFAULT_TTL;
+ }
+ break;
+ case 3: /* ipInReceives */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinreceives;
+ }
+ break;
+ case 4: /* ipInHdrErrors */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinhdrerrors;
+ }
+ break;
+ case 5: /* ipInAddrErrors */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinaddrerrors;
+ }
+ break;
+ case 6: /* ipForwDatagrams */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipforwdatagrams;
+ }
+ break;
+ case 7: /* ipInUnknownProtos */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinunknownprotos;
+ }
+ break;
+ case 8: /* ipInDiscards */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipindiscards;
+ }
+ break;
+ case 9: /* ipInDelivers */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipindelivers;
+ }
+ break;
+ case 10: /* ipOutRequests */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutrequests;
+ }
+ break;
+ case 11: /* ipOutDiscards */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutdiscards;
+ }
+ break;
+ case 12: /* ipOutNoRoutes */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutnoroutes;
+ }
+ break;
+ case 13: /* ipReasmTimeout */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_REASSEMBLY
+ *sint_ptr = IP_REASS_MAXAGE;
+#else
+ *sint_ptr = 0;
+#endif
+ }
+ break;
+ case 14: /* ipReasmReqds */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmreqds;
+ }
+ break;
+ case 15: /* ipReasmOKs */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmoks;
+ }
+ break;
+ case 16: /* ipReasmFails */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmfails;
+ }
+ break;
+ case 17: /* ipFragOKs */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragoks;
+ }
+ break;
+ case 18: /* ipFragFails */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragfails;
+ }
+ break;
+ case 19: /* ipFragCreates */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragcreates;
+ }
+ break;
+ case 23: /* ipRoutingDiscards */
+ /** @todo can lwIP discard routes at all?? hardwire this to 0?? */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = iproutingdiscards;
+ }
+ break;
+ };
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param od is the object definition
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ *
+ * @note we allow set if the value matches the hardwired value,
+ * otherwise return badvalue.
+ */
+static u8_t
+ip_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+ s32_t *sint_ptr = (s32_t*)value;
+
+ LWIP_UNUSED_ARG(len);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ if (*sint_ptr == 1)
+#else
+ /* not-forwarding */
+ if (*sint_ptr == 2)
+#endif
+ {
+ set_ok = 1;
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ if (*sint_ptr == IP_DEFAULT_TTL)
+ {
+ set_ok = 1;
+ }
+ break;
+ };
+ return set_ok;
+}
+
+static void
+ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (4) */
+ ident_len += 4;
+ ident -= 4;
+
+ if (ident_len == 5)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipAdEntAddr */
+ case 3: /* ipAdEntNetMask */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* ipAdEntIfIndex */
+ case 4: /* ipAdEntBcastAddr */
+ case 5: /* ipAdEntReasmMaxSize */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ u16_t ifidx;
+ ip_addr_t ip;
+ struct netif *netif = netif_list;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], &ip);
+ ifidx = 0;
+ while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr))
+ {
+ netif = netif->next;
+ ifidx++;
+ }
+
+ if (netif != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipAdEntAddr */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = netif->ip_addr;
+ }
+ break;
+ case 2: /* ipAdEntIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = ifidx + 1;
+ }
+ break;
+ case 3: /* ipAdEntNetMask */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = netif->netmask;
+ }
+ break;
+ case 4: /* ipAdEntBcastAddr */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ /* lwIP oddity, there's no broadcast
+ address in the netif we can rely on */
+ *sint_ptr = IPADDR_BROADCAST & 1;
+ }
+ break;
+ case 5: /* ipAdEntReasmMaxSize */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_REASSEMBLY
+ /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
+ * but only if receiving one fragmented packet at a time.
+ * The current solution is to calculate for 2 simultaneous packets...
+ */
+ *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
+ (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN)));
+#else
+ /** @todo returning MTU would be a bad thing and
+ returning a wild guess like '576' isn't good either */
+ *sint_ptr = 0;
+#endif
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * @note
+ * lwIP IP routing is currently using the network addresses in netif_list.
+ * if no suitable network IP is found in netif_list, the default_netif is used.
+ */
+static void
+ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (4) */
+ ident_len += 4;
+ ident -= 4;
+
+ if (ident_len == 5)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipRouteDest */
+ case 7: /* ipRouteNextHop */
+ case 11: /* ipRouteMask */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* ipRouteIfIndex */
+ case 3: /* ipRouteMetric1 */
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ case 8: /* ipRouteType */
+ case 10: /* ipRouteAge */
+ case 12: /* ipRouteMetric5 */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 9: /* ipRouteProto */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 13: /* ipRouteInfo */
+ /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = iprouteinfo.len * sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ ip_addr_t dest;
+ s32_t *ident;
+ u8_t id;
+
+ ident = od->id_inst_ptr;
+ snmp_oidtoip(&ident[1], &dest);
+
+ if (ip_addr_isany(&dest))
+ {
+ /* ip_route() uses default netif for default route */
+ netif = netif_default;
+ }
+ else
+ {
+ /* not using ip_route(), need exact match! */
+ netif = netif_list;
+ while ((netif != NULL) &&
+ !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) )
+ {
+ netif = netif->next;
+ }
+ }
+ if (netif != NULL)
+ {
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipRouteDest */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte has 0.0.0.0 dest */
+ ip_addr_set_zero(dst);
+ }
+ else
+ {
+ /* netifs have netaddress dest */
+ ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask);
+ }
+ }
+ break;
+ case 2: /* ipRouteIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ snmp_netiftoifindex(netif, sint_ptr);
+ }
+ break;
+ case 3: /* ipRouteMetric1 */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte has metric 1 */
+ *sint_ptr = 1;
+ }
+ else
+ {
+ /* other rtes have metric 0 */
+ *sint_ptr = 0;
+ }
+ }
+ break;
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ case 12: /* ipRouteMetric5 */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* not used */
+ *sint_ptr = -1;
+ }
+ break;
+ case 7: /* ipRouteNextHop */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte: gateway */
+ *dst = netif->gw;
+ }
+ else
+ {
+ /* other rtes: netif ip_addr */
+ *dst = netif->ip_addr;
+ }
+ }
+ break;
+ case 8: /* ipRouteType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte is indirect */
+ *sint_ptr = 4;
+ }
+ else
+ {
+ /* other rtes are direct */
+ *sint_ptr = 3;
+ }
+ }
+ break;
+ case 9: /* ipRouteProto */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* locally defined routes */
+ *sint_ptr = 2;
+ }
+ break;
+ case 10: /* ipRouteAge */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /** @todo (sysuptime - timestamp last change) / 100
+ @see snmp_insert_iprteidx_tree() */
+ *sint_ptr = 0;
+ }
+ break;
+ case 11: /* ipRouteMask */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte use 0.0.0.0 mask */
+ ip_addr_set_zero(dst);
+ }
+ else
+ {
+ /* other rtes use netmask */
+ *dst = netif->netmask;
+ }
+ }
+ break;
+ case 13: /* ipRouteInfo */
+ objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ }
+ }
+}
+
+static void
+ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipNetToMediaIfIndex */
+ case 4: /* ipNetToMediaType */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* ipNetToMediaPhysAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = 6; /** @todo try to use netif::hwaddr_len */
+ break;
+ case 3: /* ipNetToMediaNetAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+#if LWIP_ARP
+ u8_t id;
+ struct eth_addr* ethaddr_ret;
+ ip_addr_t* ipaddr_ret;
+#endif /* LWIP_ARP */
+ ip_addr_t ip;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ snmp_oidtoip(&od->id_inst_ptr[2], &ip);
+
+#if LWIP_ARP /** @todo implement a netif_find_addr */
+ if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipNetToMediaIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* ipNetToMediaPhysAddress */
+ {
+ struct eth_addr *dst = (struct eth_addr*)value;
+
+ *dst = *ethaddr_ret;
+ }
+ break;
+ case 3: /* ipNetToMediaNetAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ *dst = *ipaddr_ret;
+ }
+ break;
+ case 4: /* ipNetToMediaType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* dynamic (?) */
+ *sint_ptr = 3;
+ }
+ break;
+ }
+ }
+#endif /* LWIP_ARP */
+}
+
+static void
+icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if ((ident_len == 2) &&
+ (ident[0] > 0) && (ident[0] < 27))
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+icmp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* icmpInMsgs */
+ *uint_ptr = icmpinmsgs;
+ break;
+ case 2: /* icmpInErrors */
+ *uint_ptr = icmpinerrors;
+ break;
+ case 3: /* icmpInDestUnreachs */
+ *uint_ptr = icmpindestunreachs;
+ break;
+ case 4: /* icmpInTimeExcds */
+ *uint_ptr = icmpintimeexcds;
+ break;
+ case 5: /* icmpInParmProbs */
+ *uint_ptr = icmpinparmprobs;
+ break;
+ case 6: /* icmpInSrcQuenchs */
+ *uint_ptr = icmpinsrcquenchs;
+ break;
+ case 7: /* icmpInRedirects */
+ *uint_ptr = icmpinredirects;
+ break;
+ case 8: /* icmpInEchos */
+ *uint_ptr = icmpinechos;
+ break;
+ case 9: /* icmpInEchoReps */
+ *uint_ptr = icmpinechoreps;
+ break;
+ case 10: /* icmpInTimestamps */
+ *uint_ptr = icmpintimestamps;
+ break;
+ case 11: /* icmpInTimestampReps */
+ *uint_ptr = icmpintimestampreps;
+ break;
+ case 12: /* icmpInAddrMasks */
+ *uint_ptr = icmpinaddrmasks;
+ break;
+ case 13: /* icmpInAddrMaskReps */
+ *uint_ptr = icmpinaddrmaskreps;
+ break;
+ case 14: /* icmpOutMsgs */
+ *uint_ptr = icmpoutmsgs;
+ break;
+ case 15: /* icmpOutErrors */
+ *uint_ptr = icmpouterrors;
+ break;
+ case 16: /* icmpOutDestUnreachs */
+ *uint_ptr = icmpoutdestunreachs;
+ break;
+ case 17: /* icmpOutTimeExcds */
+ *uint_ptr = icmpouttimeexcds;
+ break;
+ case 18: /* icmpOutParmProbs */
+ *uint_ptr = icmpoutparmprobs;
+ break;
+ case 19: /* icmpOutSrcQuenchs */
+ *uint_ptr = icmpoutsrcquenchs;
+ break;
+ case 20: /* icmpOutRedirects */
+ *uint_ptr = icmpoutredirects;
+ break;
+ case 21: /* icmpOutEchos */
+ *uint_ptr = icmpoutechos;
+ break;
+ case 22: /* icmpOutEchoReps */
+ *uint_ptr = icmpoutechoreps;
+ break;
+ case 23: /* icmpOutTimestamps */
+ *uint_ptr = icmpouttimestamps;
+ break;
+ case 24: /* icmpOutTimestampReps */
+ *uint_ptr = icmpouttimestampreps;
+ break;
+ case 25: /* icmpOutAddrMasks */
+ *uint_ptr = icmpoutaddrmasks;
+ break;
+ case 26: /* icmpOutAddrMaskReps */
+ *uint_ptr = icmpoutaddrmaskreps;
+ break;
+ }
+}
+
+#if LWIP_TCP
+/** @todo tcp grp */
+static void
+tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
+
+ switch (id)
+ {
+ case 1: /* tcpRtoAlgorithm */
+ case 2: /* tcpRtoMin */
+ case 3: /* tcpRtoMax */
+ case 4: /* tcpMaxConn */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 5: /* tcpActiveOpens */
+ case 6: /* tcpPassiveOpens */
+ case 7: /* tcpAttemptFails */
+ case 8: /* tcpEstabResets */
+ case 10: /* tcpInSegs */
+ case 11: /* tcpOutSegs */
+ case 12: /* tcpRetransSegs */
+ case 14: /* tcpInErrs */
+ case 15: /* tcpOutRsts */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 9: /* tcpCurrEstab */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
+ od->v_len = sizeof(u32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+tcp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ s32_t *sint_ptr = (s32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* tcpRtoAlgorithm, vanj(4) */
+ *sint_ptr = 4;
+ break;
+ case 2: /* tcpRtoMin */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 1000;
+ break;
+ case 3: /* tcpRtoMax */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 60000;
+ break;
+ case 4: /* tcpMaxConn */
+ *sint_ptr = MEMP_NUM_TCP_PCB;
+ break;
+ case 5: /* tcpActiveOpens */
+ *uint_ptr = tcpactiveopens;
+ break;
+ case 6: /* tcpPassiveOpens */
+ *uint_ptr = tcppassiveopens;
+ break;
+ case 7: /* tcpAttemptFails */
+ *uint_ptr = tcpattemptfails;
+ break;
+ case 8: /* tcpEstabResets */
+ *uint_ptr = tcpestabresets;
+ break;
+ case 9: /* tcpCurrEstab */
+ {
+ u16_t tcpcurrestab = 0;
+ struct tcp_pcb *pcb = tcp_active_pcbs;
+ while (pcb != NULL)
+ {
+ if ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))
+ {
+ tcpcurrestab++;
+ }
+ pcb = pcb->next;
+ }
+ *uint_ptr = tcpcurrestab;
+ }
+ break;
+ case 10: /* tcpInSegs */
+ *uint_ptr = tcpinsegs;
+ break;
+ case 11: /* tcpOutSegs */
+ *uint_ptr = tcpoutsegs;
+ break;
+ case 12: /* tcpRetransSegs */
+ *uint_ptr = tcpretranssegs;
+ break;
+ case 14: /* tcpInErrs */
+ *uint_ptr = tcpinerrs;
+ break;
+ case 15: /* tcpOutRsts */
+ *uint_ptr = tcpoutrsts;
+ break;
+ }
+}
+#ifdef THIS_SEEMS_UNUSED
+static void
+tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (10) */
+ ident_len += 10;
+ ident -= 10;
+
+ if (ident_len == 11)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ id = ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
+
+ switch (id)
+ {
+ case 1: /* tcpConnState */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* tcpConnLocalAddress */
+ case 4: /* tcpConnRemAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 3: /* tcpConnLocalPort */
+ case 5: /* tcpConnRemPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ ip_addr_t lip, rip;
+ u16_t lport, rport;
+ s32_t *ident;
+
+ ident = od->id_inst_ptr;
+ snmp_oidtoip(&ident[1], &lip);
+ lport = ident[5];
+ snmp_oidtoip(&ident[6], &rip);
+ rport = ident[10];
+
+ /** @todo find matching PCB */
+}
+#endif /* if 0 */
+#endif
+
+static void
+udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if ((ident_len == 2) &&
+ (ident[0] > 0) && (ident[0] < 6))
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpInDatagrams */
+ *uint_ptr = udpindatagrams;
+ break;
+ case 2: /* udpNoPorts */
+ *uint_ptr = udpnoports;
+ break;
+ case 3: /* udpInErrors */
+ *uint_ptr = udpinerrors;
+ break;
+ case 4: /* udpOutDatagrams */
+ *uint_ptr = udpoutdatagrams;
+ break;
+ }
+}
+
+static void
+udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* udpLocalAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* udpLocalPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udpentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ struct udp_pcb *pcb;
+ ip_addr_t ip;
+ u16_t port;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], &ip);
+ LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff));
+ port = (u16_t)od->id_inst_ptr[5];
+
+ pcb = udp_pcbs;
+ while ((pcb != NULL) &&
+ !(ip_addr_cmp(&pcb->local_ip, &ip) &&
+ (pcb->local_port == port)))
+ {
+ pcb = pcb->next;
+ }
+
+ if (pcb != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpLocalAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = pcb->local_ip;
+ }
+ break;
+ case 2: /* udpLocalPort */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = pcb->local_port;
+ }
+ break;
+ }
+ }
+}
+
+static void
+snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* snmpInPkts */
+ case 2: /* snmpOutPkts */
+ case 3: /* snmpInBadVersions */
+ case 4: /* snmpInBadCommunityNames */
+ case 5: /* snmpInBadCommunityUses */
+ case 6: /* snmpInASNParseErrs */
+ case 8: /* snmpInTooBigs */
+ case 9: /* snmpInNoSuchNames */
+ case 10: /* snmpInBadValues */
+ case 11: /* snmpInReadOnlys */
+ case 12: /* snmpInGenErrs */
+ case 13: /* snmpInTotalReqVars */
+ case 14: /* snmpInTotalSetVars */
+ case 15: /* snmpInGetRequests */
+ case 16: /* snmpInGetNexts */
+ case 17: /* snmpInSetRequests */
+ case 18: /* snmpInGetResponses */
+ case 19: /* snmpInTraps */
+ case 20: /* snmpOutTooBigs */
+ case 21: /* snmpOutNoSuchNames */
+ case 22: /* snmpOutBadValues */
+ case 24: /* snmpOutGenErrs */
+ case 25: /* snmpOutGetRequests */
+ case 26: /* snmpOutGetNexts */
+ case 27: /* snmpOutSetRequests */
+ case 28: /* snmpOutGetResponses */
+ case 29: /* snmpOutTraps */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+snmp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* snmpInPkts */
+ *uint_ptr = snmpinpkts;
+ break;
+ case 2: /* snmpOutPkts */
+ *uint_ptr = snmpoutpkts;
+ break;
+ case 3: /* snmpInBadVersions */
+ *uint_ptr = snmpinbadversions;
+ break;
+ case 4: /* snmpInBadCommunityNames */
+ *uint_ptr = snmpinbadcommunitynames;
+ break;
+ case 5: /* snmpInBadCommunityUses */
+ *uint_ptr = snmpinbadcommunityuses;
+ break;
+ case 6: /* snmpInASNParseErrs */
+ *uint_ptr = snmpinasnparseerrs;
+ break;
+ case 8: /* snmpInTooBigs */
+ *uint_ptr = snmpintoobigs;
+ break;
+ case 9: /* snmpInNoSuchNames */
+ *uint_ptr = snmpinnosuchnames;
+ break;
+ case 10: /* snmpInBadValues */
+ *uint_ptr = snmpinbadvalues;
+ break;
+ case 11: /* snmpInReadOnlys */
+ *uint_ptr = snmpinreadonlys;
+ break;
+ case 12: /* snmpInGenErrs */
+ *uint_ptr = snmpingenerrs;
+ break;
+ case 13: /* snmpInTotalReqVars */
+ *uint_ptr = snmpintotalreqvars;
+ break;
+ case 14: /* snmpInTotalSetVars */
+ *uint_ptr = snmpintotalsetvars;
+ break;
+ case 15: /* snmpInGetRequests */
+ *uint_ptr = snmpingetrequests;
+ break;
+ case 16: /* snmpInGetNexts */
+ *uint_ptr = snmpingetnexts;
+ break;
+ case 17: /* snmpInSetRequests */
+ *uint_ptr = snmpinsetrequests;
+ break;
+ case 18: /* snmpInGetResponses */
+ *uint_ptr = snmpingetresponses;
+ break;
+ case 19: /* snmpInTraps */
+ *uint_ptr = snmpintraps;
+ break;
+ case 20: /* snmpOutTooBigs */
+ *uint_ptr = snmpouttoobigs;
+ break;
+ case 21: /* snmpOutNoSuchNames */
+ *uint_ptr = snmpoutnosuchnames;
+ break;
+ case 22: /* snmpOutBadValues */
+ *uint_ptr = snmpoutbadvalues;
+ break;
+ case 24: /* snmpOutGenErrs */
+ *uint_ptr = snmpoutgenerrs;
+ break;
+ case 25: /* snmpOutGetRequests */
+ *uint_ptr = snmpoutgetrequests;
+ break;
+ case 26: /* snmpOutGetNexts */
+ *uint_ptr = snmpoutgetnexts;
+ break;
+ case 27: /* snmpOutSetRequests */
+ *uint_ptr = snmpoutsetrequests;
+ break;
+ case 28: /* snmpOutGetResponses */
+ *uint_ptr = snmpoutgetresponses;
+ break;
+ case 29: /* snmpOutTraps */
+ *uint_ptr = snmpouttraps;
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ *uint_ptr = *snmpenableauthentraps_ptr;
+ break;
+ };
+}
+
+/**
+ * Test snmp object value before setting.
+ *
+ * @param od is the object definition
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ */
+static u8_t
+snmp_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+
+ LWIP_UNUSED_ARG(len);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ if (id == 30)
+ {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default)
+ {
+ /* we should have writable non-volatile mem here */
+ if ((*sint_ptr == 1) || (*sint_ptr == 2))
+ {
+ set_ok = 1;
+ }
+ }
+ else
+ {
+ /* const or hardwired value */
+ if (*sint_ptr == snmpenableauthentraps_default)
+ {
+ set_ok = 1;
+ }
+ }
+ }
+ return set_ok;
+}
+
+static void
+snmp_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ if (id == 30)
+ {
+ /* snmpEnableAuthenTraps */
+ /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */
+ u8_t *ptr = (u8_t*)value;
+ *snmpenableauthentraps_ptr = *ptr;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/core/lwip/src/core/snmp/mib_structs.c b/core/lwip/src/core/snmp/mib_structs.c
new file mode 100644
index 00000000..2f185cb4
--- /dev/null
+++ b/core/lwip/src/core/snmp/mib_structs.c
@@ -0,0 +1,1174 @@
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_structs.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+
+/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
+const s32_t prefix[4] = {1, 3, 6, 1};
+
+#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
+/** node stack entry (old news?) */
+struct nse
+{
+ /** right child */
+ struct mib_node* r_ptr;
+ /** right child identifier */
+ s32_t r_id;
+ /** right child next level */
+ u8_t r_nl;
+};
+static u8_t node_stack_cnt;
+static struct nse node_stack[NODE_STACK_SIZE];
+
+/**
+ * Pushes nse struct onto stack.
+ */
+static void
+push_node(struct nse* node)
+{
+ LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id));
+ if (node_stack_cnt < NODE_STACK_SIZE)
+ {
+ node_stack[node_stack_cnt] = *node;
+ node_stack_cnt++;
+ }
+}
+
+/**
+ * Pops nse struct from stack.
+ */
+static void
+pop_node(struct nse* node)
+{
+ if (node_stack_cnt > 0)
+ {
+ node_stack_cnt--;
+ *node = node_stack[node_stack_cnt];
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id));
+}
+
+/**
+ * Conversion from ifIndex to lwIP netif
+ * @param ifindex is a s32_t object sub-identifier
+ * @param netif points to returned netif struct pointer
+ */
+void
+snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
+{
+ struct netif *nif = netif_list;
+ s32_t i, ifidx;
+
+ ifidx = ifindex - 1;
+ i = 0;
+ while ((nif != NULL) && (i < ifidx))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *netif = nif;
+}
+
+/**
+ * Conversion from lwIP netif to ifIndex
+ * @param netif points to a netif struct
+ * @param ifidx points to s32_t object sub-identifier
+ */
+void
+snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
+{
+ struct netif *nif = netif_list;
+ u16_t i;
+
+ i = 0;
+ while ((nif != NULL) && (nif != netif))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *ifidx = i+1;
+}
+
+/**
+ * Conversion from oid to lwIP ip_addr
+ * @param ident points to s32_t ident[4] input
+ * @param ip points to output struct
+ */
+void
+snmp_oidtoip(s32_t *ident, ip_addr_t *ip)
+{
+ IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
+}
+
+/**
+ * Conversion from lwIP ip_addr to oid
+ * @param ip points to input struct
+ * @param ident points to s32_t ident[4] output
+ */
+void
+snmp_iptooid(ip_addr_t *ip, s32_t *ident)
+{
+ ident[0] = ip4_addr1(ip);
+ ident[1] = ip4_addr2(ip);
+ ident[2] = ip4_addr3(ip);
+ ident[3] = ip4_addr4(ip);
+}
+
+struct mib_list_node *
+snmp_mib_ln_alloc(s32_t id)
+{
+ struct mib_list_node *ln;
+
+ ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE);
+ if (ln != NULL)
+ {
+ ln->prev = NULL;
+ ln->next = NULL;
+ ln->objid = id;
+ ln->nptr = NULL;
+ }
+ return ln;
+}
+
+void
+snmp_mib_ln_free(struct mib_list_node *ln)
+{
+ memp_free(MEMP_SNMP_NODE, ln);
+}
+
+struct mib_list_rootnode *
+snmp_mib_lrn_alloc(void)
+{
+ struct mib_list_rootnode *lrn;
+
+ lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE);
+ if (lrn != NULL)
+ {
+ lrn->get_object_def = noleafs_get_object_def;
+ lrn->get_value = noleafs_get_value;
+ lrn->set_test = noleafs_set_test;
+ lrn->set_value = noleafs_set_value;
+ lrn->node_type = MIB_NODE_LR;
+ lrn->maxlength = 0;
+ lrn->head = NULL;
+ lrn->tail = NULL;
+ lrn->count = 0;
+ }
+ return lrn;
+}
+
+void
+snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
+{
+ memp_free(MEMP_SNMP_ROOTNODE, lrn);
+}
+
+/**
+ * Inserts node in idx list in a sorted
+ * (ascending order) fashion and
+ * allocates the node if needed.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param insn points to a pointer to the inserted node
+ * used for constructing the tree.
+ * @return -1 if failed, 1 if inserted, 2 if present.
+ */
+s8_t
+snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
+{
+ struct mib_list_node *nn;
+ s8_t insert;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+
+ /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
+ insert = 0;
+ if (rn->head == NULL)
+ {
+ /* empty list, add first node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ rn->head = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ insert = -1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *n;
+ /* at least one node is present */
+ n = rn->head;
+ while ((n != NULL) && (insert == 0))
+ {
+ if (n->objid == objid)
+ {
+ /* node is already there */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
+ *insn = n;
+ insert = 2;
+ }
+ else if (n->objid < objid)
+ {
+ if (n->next == NULL)
+ {
+ /* alloc and insert at the tail */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ nn->next = NULL;
+ nn->prev = n;
+ n->next = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ else
+ {
+ /* there's more to explore: traverse list */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
+ n = n->next;
+ }
+ }
+ else
+ {
+ /* n->objid > objid */
+ /* alloc and insert between n->prev and n */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ if (n->prev == NULL)
+ {
+ /* insert at the head */
+ nn->next = n;
+ nn->prev = NULL;
+ rn->head = nn;
+ n->prev = nn;
+ }
+ else
+ {
+ /* insert in the middle */
+ nn->next = n;
+ nn->prev = n->prev;
+ n->prev->next = nn;
+ n->prev = nn;
+ }
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ }
+ }
+ if (insert == 1)
+ {
+ rn->count += 1;
+ }
+ LWIP_ASSERT("insert != 0",insert != 0);
+ return insert;
+}
+
+/**
+ * Finds node in idx list and returns deletion mark.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param fn returns pointer to found node
+ * @return 0 if not found, 1 if deletable,
+ * 2 can't delete (2 or more children), 3 not a list_node
+ */
+s8_t
+snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
+{
+ s8_t fc;
+ struct mib_list_node *n;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ n = rn->head;
+ while ((n != NULL) && (n->objid != objid))
+ {
+ n = n->next;
+ }
+ if (n == NULL)
+ {
+ fc = 0;
+ }
+ else if (n->nptr == NULL)
+ {
+ /* leaf, can delete node */
+ fc = 1;
+ }
+ else
+ {
+ struct mib_list_rootnode *r;
+
+ if (n->nptr->node_type == MIB_NODE_LR)
+ {
+ r = (struct mib_list_rootnode *)n->nptr;
+ if (r->count > 1)
+ {
+ /* can't delete node */
+ fc = 2;
+ }
+ else
+ {
+ /* count <= 1, can delete node */
+ fc = 1;
+ }
+ }
+ else
+ {
+ /* other node type */
+ fc = 3;
+ }
+ }
+ *fn = n;
+ return fc;
+}
+
+/**
+ * Removes node from idx list
+ * if it has a single child left.
+ *
+ * @param rn points to the root node
+ * @param n points to the node to delete
+ * @return the nptr to be freed by caller
+ */
+struct mib_list_rootnode *
+snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
+{
+ struct mib_list_rootnode *next;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ LWIP_ASSERT("n != NULL",n != NULL);
+
+ /* caller must remove this sub-tree */
+ next = (struct mib_list_rootnode*)(n->nptr);
+ rn->count -= 1;
+
+ if (n == rn->head)
+ {
+ rn->head = n->next;
+ if (n->next != NULL)
+ {
+ /* not last node, new list begin */
+ n->next->prev = NULL;
+ }
+ }
+ else if (n == rn->tail)
+ {
+ rn->tail = n->prev;
+ if (n->prev != NULL)
+ {
+ /* not last node, new list end */
+ n->prev->next = NULL;
+ }
+ }
+ else
+ {
+ /* node must be in the middle */
+ n->prev->next = n->next;
+ n->next->prev = n->prev;
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
+ snmp_mib_ln_free(n);
+ if (rn->count == 0)
+ {
+ rn->head = NULL;
+ rn->tail = NULL;
+ }
+ return next;
+}
+
+
+
+/**
+ * Searches tree for the supplied (scalar?) object identifier.
+ *
+ * @param node points to the root of the tree ('.internet')
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param np points to the found object instance (return)
+ * @return pointer to the requested parent (!) node if success, NULL otherwise
+ */
+struct mib_node *
+snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np)
+{
+ u8_t node_type, ext_level;
+
+ ext_level = 0;
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident));
+ while (node != NULL)
+ {
+ node_type = node->node_type;
+ if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ u16_t i;
+
+ if (ident_len > 0)
+ {
+ /* array node (internal ROM or RAM, fixed length) */
+ an = (struct mib_array_node *)node;
+ i = 0;
+ while ((i < an->maxlength) && (an->objid[i] != *ident))
+ {
+ i++;
+ }
+ if (i < an->maxlength)
+ {
+ /* found it, if available proceed to child, otherwise inspect leaf */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
+ if (an->nptr[i] == NULL)
+ {
+ /* a scalar leaf OR table,
+ inspect remaining instance number / table index */
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* follow next child pointer */
+ ident++;
+ ident_len--;
+ node = an->nptr[i];
+ }
+ }
+ else
+ {
+ /* search failed, identifier mismatch (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if(node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ struct mib_list_node *ln;
+
+ if (ident_len > 0)
+ {
+ /* list root node (internal 'RAM', variable length) */
+ lrn = (struct mib_list_rootnode *)node;
+ ln = lrn->head;
+ /* iterate over list, head to tail */
+ while ((ln != NULL) && (ln->objid != *ident))
+ {
+ ln = ln->next;
+ }
+ if (ln != NULL)
+ {
+ /* found it, proceed to child */;
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
+ if (ln->nptr == NULL)
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* follow next child pointer */
+ ident_len--;
+ ident++;
+ node = ln->nptr;
+ }
+ }
+ else
+ {
+ /* search failed */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if(node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ u16_t i, len;
+
+ if (ident_len > 0)
+ {
+ /* external node (addressing and access via functions) */
+ en = (struct mib_external_node *)node;
+
+ i = 0;
+ len = en->level_length(en->addr_inf,ext_level);
+ while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0))
+ {
+ i++;
+ }
+ if (i < len)
+ {
+ s32_t debug_id;
+
+ en->get_objid(en->addr_inf,ext_level,i,&debug_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident));
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* found it, proceed to child */
+ ident_len--;
+ ident++;
+ ext_level++;
+ }
+ }
+ else
+ {
+ /* search failed */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if (node_type == MIB_NODE_SC)
+ {
+ mib_scalar_node *sn;
+
+ sn = (mib_scalar_node *)node;
+ if ((ident_len == 1) && (*ident == 0))
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)sn;
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n"));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* unknown node_type */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type));
+ return NULL;
+ }
+ }
+ /* done, found nothing */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node));
+ return NULL;
+}
+
+/**
+ * Test table for presence of at least one table entry.
+ */
+static u8_t
+empty_table(struct mib_node *node)
+{
+ u8_t node_type;
+ u8_t empty = 0;
+
+ if (node != NULL)
+ {
+ node_type = node->node_type;
+ if (node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ lrn = (struct mib_list_rootnode *)node;
+ if ((lrn->count == 0) || (lrn->head == NULL))
+ {
+ empty = 1;
+ }
+ }
+ else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ an = (struct mib_array_node *)node;
+ if ((an->maxlength == 0) || (an->nptr == NULL))
+ {
+ empty = 1;
+ }
+ }
+ else if (node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ en = (struct mib_external_node *)node;
+ if (en->tree_levels == 0)
+ {
+ empty = 1;
+ }
+ }
+ }
+ return empty;
+}
+
+/**
+ * Tree expansion.
+ */
+struct mib_node *
+snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
+{
+ u8_t node_type, ext_level, climb_tree;
+
+ ext_level = 0;
+ /* reset node stack */
+ node_stack_cnt = 0;
+ while (node != NULL)
+ {
+ climb_tree = 0;
+ node_type = node->node_type;
+ if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ u16_t i;
+
+ /* array node (internal ROM or RAM, fixed length) */
+ an = (struct mib_array_node *)node;
+ if (ident_len > 0)
+ {
+ i = 0;
+ while ((i < an->maxlength) && (an->objid[i] < *ident))
+ {
+ i++;
+ }
+ if (i < an->maxlength)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
+ /* add identifier to oidret */
+ oidret->id[oidret->len] = an->objid[i];
+ (oidret->len)++;
+
+ if (an->nptr[i] == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
+ /* leaf node (e.g. in a fixed size table) */
+ if (an->objid[i] > *ident)
+ {
+ return (struct mib_node*)an;
+ }
+ else if ((i + 1) < an->maxlength)
+ {
+ /* an->objid[i] == *ident */
+ (oidret->len)--;
+ oidret->id[oidret->len] = an->objid[i + 1];
+ (oidret->len)++;
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* (i + 1) == an->maxlength */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ struct nse cur_node;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = (u8_t)i + 1;
+ while ((j < an->maxlength) && (empty_table(an->nptr[j])))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ cur_node.r_ptr = an->nptr[j];
+ cur_node.r_id = an->objid[j];
+ cur_node.r_nl = 0;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (an->objid[i] == *ident)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* an->objid[i] < *ident */
+ ident_len = 0;
+ }
+ /* follow next child pointer */
+ node = an->nptr[i];
+ }
+ }
+ else
+ {
+ /* i == an->maxlength */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ /* ident_len == 0, complete with leftmost '.thing' */
+ j = 0;
+ while ((j < an->maxlength) && empty_table(an->nptr[j]))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j]));
+ oidret->id[oidret->len] = an->objid[j];
+ (oidret->len)++;
+ if (an->nptr[j] == NULL)
+ {
+ /* leaf node */
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* no leaf, continue */
+ node = an->nptr[j];
+ }
+ }
+ else
+ {
+ /* j == an->maxlength */
+ climb_tree = 1;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ struct mib_list_node *ln;
+
+ /* list root node (internal 'RAM', variable length) */
+ lrn = (struct mib_list_rootnode *)node;
+ if (ident_len > 0)
+ {
+ ln = lrn->head;
+ /* iterate over list, head to tail */
+ while ((ln != NULL) && (ln->objid < *ident))
+ {
+ ln = ln->next;
+ }
+ if (ln != NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
+ oidret->id[oidret->len] = ln->objid;
+ (oidret->len)++;
+ if (ln->nptr == NULL)
+ {
+ /* leaf node */
+ if (ln->objid > *ident)
+ {
+ return (struct mib_node*)lrn;
+ }
+ else if (ln->next != NULL)
+ {
+ /* ln->objid == *ident */
+ (oidret->len)--;
+ oidret->id[oidret->len] = ln->next->objid;
+ (oidret->len)++;
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* ln->next == NULL */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *jn;
+ struct nse cur_node;
+
+ /* non-leaf, store right child ptr and id */
+ jn = ln->next;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ cur_node.r_ptr = jn->nptr;
+ cur_node.r_id = jn->objid;
+ cur_node.r_nl = 0;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (ln->objid == *ident)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* ln->objid < *ident */
+ ident_len = 0;
+ }
+ /* follow next child pointer */
+ node = ln->nptr;
+ }
+
+ }
+ else
+ {
+ /* ln == NULL */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *jn;
+ /* ident_len == 0, complete with leftmost '.thing' */
+ jn = lrn->head;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid));
+ oidret->id[oidret->len] = jn->objid;
+ (oidret->len)++;
+ if (jn->nptr == NULL)
+ {
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n"));
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* no leaf, continue */
+ node = jn->nptr;
+ }
+ }
+ else
+ {
+ /* jn == NULL */
+ climb_tree = 1;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ s32_t ex_id;
+
+ /* external node (addressing and access via functions) */
+ en = (struct mib_external_node *)node;
+ if (ident_len > 0)
+ {
+ u16_t i, len;
+
+ i = 0;
+ len = en->level_length(en->addr_inf,ext_level);
+ while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0))
+ {
+ i++;
+ }
+ if (i < len)
+ {
+ /* add identifier to oidret */
+ en->get_objid(en->addr_inf,ext_level,i,&ex_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident));
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
+ /* leaf node */
+ if (ex_id > *ident)
+ {
+ return (struct mib_node*)en;
+ }
+ else if ((i + 1) < len)
+ {
+ /* ex_id == *ident */
+ en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id);
+ (oidret->len)--;
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* (i + 1) == len */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ struct nse cur_node;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = (u8_t)i + 1;
+ if (j < len)
+ {
+ /* right node is the current external node */
+ cur_node.r_ptr = node;
+ en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id);
+ cur_node.r_nl = ext_level + 1;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* external id < *ident */
+ ident_len = 0;
+ }
+ /* proceed to child */
+ ext_level++;
+ }
+ }
+ else
+ {
+ /* i == len (en->level_len()) */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ /* ident_len == 0, complete with leftmost '.thing' */
+ en->get_objid(en->addr_inf,ext_level,0,&ex_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id));
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n"));
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* no leaf, proceed to child */
+ ext_level++;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_SC)
+ {
+ mib_scalar_node *sn;
+
+ /* scalar node */
+ sn = (mib_scalar_node *)node;
+ if (ident_len > 0)
+ {
+ /* at .0 */
+ climb_tree = 1;
+ }
+ else
+ {
+ /* ident_len == 0, complete object identifier */
+ oidret->id[oidret->len] = 0;
+ (oidret->len)++;
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n"));
+ return (struct mib_node*)sn;
+ }
+ }
+ else
+ {
+ /* unknown/unhandled node_type */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type));
+ return NULL;
+ }
+
+ if (climb_tree)
+ {
+ struct nse child;
+
+ /* find right child ptr */
+ child.r_ptr = NULL;
+ child.r_id = 0;
+ child.r_nl = 0;
+ while ((node_stack_cnt > 0) && (child.r_ptr == NULL))
+ {
+ pop_node(&child);
+ /* trim returned oid */
+ (oidret->len)--;
+ }
+ if (child.r_ptr != NULL)
+ {
+ /* incoming ident is useless beyond this point */
+ ident_len = 0;
+ oidret->id[oidret->len] = child.r_id;
+ oidret->len++;
+ node = child.r_ptr;
+ ext_level = child.r_nl;
+ }
+ else
+ {
+ /* tree ends here ... */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n"));
+ return NULL;
+ }
+ }
+ }
+ /* done, found nothing */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node));
+ return NULL;
+}
+
+/**
+ * Test object identifier for the iso.org.dod.internet prefix.
+ *
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @return 1 if it matches, 0 otherwise
+ */
+u8_t
+snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident)
+{
+ if ((ident_len > 3) &&
+ (ident[0] == 1) && (ident[1] == 3) &&
+ (ident[2] == 6) && (ident[3] == 1))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * Expands object identifier to the iso.org.dod.internet
+ * prefix for use in getnext operation.
+ *
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param oidret points to returned expanded object identifier
+ * @return 1 if it matches, 0 otherwise
+ *
+ * @note ident_len 0 is allowed, expanding to the first known object id!!
+ */
+u8_t
+snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
+{
+ const s32_t *prefix_ptr;
+ s32_t *ret_ptr;
+ u8_t i;
+
+ i = 0;
+ prefix_ptr = &prefix[0];
+ ret_ptr = &oidret->id[0];
+ ident_len = ((ident_len < 4)?ident_len:4);
+ while ((i < ident_len) && ((*ident) <= (*prefix_ptr)))
+ {
+ *ret_ptr++ = *prefix_ptr++;
+ ident++;
+ i++;
+ }
+ if (i == ident_len)
+ {
+ /* match, complete missing bits */
+ while (i < 4)
+ {
+ *ret_ptr++ = *prefix_ptr++;
+ i++;
+ }
+ oidret->len = i;
+ return 1;
+ }
+ else
+ {
+ /* i != ident_len */
+ return 0;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/core/lwip/src/core/snmp/msg_in.c b/core/lwip/src/core/snmp/msg_in.c
new file mode 100644
index 00000000..2dfb55b2
--- /dev/null
+++ b/core/lwip/src/core/snmp/msg_in.c
@@ -0,0 +1,1437 @@
+/**
+ * @file
+ * SNMP input message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/ip_addr.h"
+#include "lwip/memp.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+/* public (non-static) constants */
+/** SNMP v1 == 0 */
+const s32_t snmp_version = 0;
+/** default SNMP community string */
+const char snmp_publiccommunity[7] = "public";
+
+/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
+struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
+/* UDP Protocol Control Block */
+struct udp_pcb *snmp1_pcb;
+
+static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
+static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
+
+
+/**
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
+ */
+void
+snmp_init(void)
+{
+ struct snmp_msg_pstat *msg_ps;
+ u8_t i;
+
+ snmp1_pcb = udp_new();
+ if (snmp1_pcb != NULL)
+ {
+ udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
+ udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
+ }
+ msg_ps = &msg_input_list[0];
+ for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
+ {
+ msg_ps->state = SNMP_MSG_EMPTY;
+ msg_ps->error_index = 0;
+ msg_ps->error_status = SNMP_ES_NOERROR;
+ msg_ps++;
+ }
+ trap_msg.pcb = snmp1_pcb;
+
+#ifdef SNMP_PRIVATE_MIB_INIT
+ /* If defined, rhis must be a function-like define to initialize the
+ * private MIB after the stack has been initialized.
+ * The private MIB can also be initialized in tcpip_callback (or after
+ * the stack is initialized), this define is only for convenience. */
+ SNMP_PRIVATE_MIB_INIT();
+#endif /* SNMP_PRIVATE_MIB_INIT */
+
+ /* The coldstart trap will only be output
+ if our outgoing interface is up & configured */
+ snmp_coldstart_trap();
+}
+
+static void
+snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
+{
+ snmp_varbind_list_free(&msg_ps->outvb);
+ msg_ps->outvb = msg_ps->invb;
+ msg_ps->invb.head = NULL;
+ msg_ps->invb.tail = NULL;
+ msg_ps->invb.count = 0;
+ msg_ps->error_status = error;
+ msg_ps->error_index = 1 + msg_ps->vb_idx;
+ snmp_send_response(msg_ps);
+ snmp_varbind_list_free(&msg_ps->outvb);
+ msg_ps->state = SNMP_MSG_EMPTY;
+}
+
+static void
+snmp_ok_response(struct snmp_msg_pstat *msg_ps)
+{
+ err_t err_ret;
+
+ err_ret = snmp_send_response(msg_ps);
+ if (err_ret == ERR_MEM)
+ {
+ /* serious memory problem, can't return tooBig */
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
+ }
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&msg_ps->invb);
+ snmp_varbind_list_free(&msg_ps->outvb);
+ msg_ps->state = SNMP_MSG_EMPTY;
+}
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ * @param msg_ps points to the assosicated message process state
+ */
+static void
+snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
+{
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
+
+ if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
+ {
+ struct mib_external_node *en;
+ struct snmp_name_ptr np;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+ np = msg_ps->ext_name_ptr;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
+ if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
+ (msg_ps->ext_object_def.access & MIB_ACCESS_READ))
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
+ en->get_value_q(request_id, &msg_ps->ext_object_def);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, np.ident_len, np.ident);
+ /* search failed, object id points to unknown object (nosuchname) */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
+ {
+ struct mib_external_node *en;
+ struct snmp_varbind *vb;
+
+ /* get_value() answer */
+ en = msg_ps->ext_mib_node;
+
+ /* allocate output varbind */
+ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
+ LWIP_ASSERT("vb != NULL",vb != NULL);
+ if (vb != NULL)
+ {
+ vb->next = NULL;
+ vb->prev = NULL;
+
+ /* move name from invb to outvb */
+ vb->ident = msg_ps->vb_ptr->ident;
+ vb->ident_len = msg_ps->vb_ptr->ident_len;
+ /* ensure this memory is refereced once only */
+ msg_ps->vb_ptr->ident = NULL;
+ msg_ps->vb_ptr->ident_len = 0;
+
+ vb->value_type = msg_ps->ext_object_def.asn_type;
+ LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
+ vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
+ if (vb->value_len > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
+ vb->value = memp_malloc(MEMP_SNMP_VALUE);
+ LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
+ if (vb->value != NULL)
+ {
+ en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ /* search again (if vb_idx < msg_ps->invb.count) */
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ en->get_value_pc(request_id, &msg_ps->ext_object_def);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
+ msg_ps->vb_ptr->ident = vb->ident;
+ msg_ps->vb_ptr->ident_len = vb->ident_len;
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ else
+ {
+ /* vb->value_len == 0, empty value (e.g. empty string) */
+ en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
+ vb->value = NULL;
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ /* search again (if vb_idx < msg_ps->invb.count) */
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ }
+ else
+ {
+ en->get_value_pc(request_id, &msg_ps->ext_object_def);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+
+ while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_name_ptr np;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ /** test object identifier for .iso.org.dod.internet prefix */
+ if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident))
+ {
+ mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &np);
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_name_ptr = np;
+
+ en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
+ mn->get_object_def(np.ident_len, np.ident, &object_def);
+ if ((object_def.instance != MIB_OBJECT_NONE) &&
+ (object_def.access & MIB_ACCESS_READ))
+ {
+ mn = mn;
+ }
+ else
+ {
+ /* search failed, object id points to unknown object (nosuchname) */
+ mn = NULL;
+ }
+ if (mn != NULL)
+ {
+ struct snmp_varbind *vb;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
+ /* allocate output varbind */
+ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
+ LWIP_ASSERT("vb != NULL",vb != NULL);
+ if (vb != NULL)
+ {
+ vb->next = NULL;
+ vb->prev = NULL;
+
+ /* move name from invb to outvb */
+ vb->ident = msg_ps->vb_ptr->ident;
+ vb->ident_len = msg_ps->vb_ptr->ident_len;
+ /* ensure this memory is refereced once only */
+ msg_ps->vb_ptr->ident = NULL;
+ msg_ps->vb_ptr->ident_len = 0;
+
+ vb->value_type = object_def.asn_type;
+ LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
+ vb->value_len = (u8_t)object_def.v_len;
+ if (vb->value_len > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
+ vb->value_len <= SNMP_MAX_VALUE_SIZE);
+ vb->value = memp_malloc(MEMP_SNMP_VALUE);
+ LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
+ if (vb->value != NULL)
+ {
+ mn->get_value(&object_def, vb->value_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
+ msg_ps->vb_ptr->ident = vb->ident;
+ msg_ps->vb_ptr->ident_len = vb->ident_len;
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ else
+ {
+ /* vb->value_len == 0, empty value (e.g. empty string) */
+ vb->value = NULL;
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ mn = NULL;
+ }
+ if (mn == NULL)
+ {
+ /* mn == NULL, noSuchName */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ snmp_ok_response(msg_ps);
+ }
+}
+
+/**
+ * Service an internal or external event for SNMP GETNEXT.
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ * @param msg_ps points to the assosicated message process state
+ */
+static void
+snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
+{
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
+
+ if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
+ {
+ struct mib_external_node *en;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
+ if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
+ en->get_value_q(request_id, &msg_ps->ext_object_def);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
+ /* search failed, object id points to unknown object (nosuchname) */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
+ {
+ struct mib_external_node *en;
+ struct snmp_varbind *vb;
+
+ /* get_value() answer */
+ en = msg_ps->ext_mib_node;
+
+ LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
+ vb = snmp_varbind_alloc(&msg_ps->ext_oid,
+ msg_ps->ext_object_def.asn_type,
+ (u8_t)msg_ps->ext_object_def.v_len);
+ if (vb != NULL)
+ {
+ en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ en->get_value_pc(request_id, &msg_ps->ext_object_def);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+
+ while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_obj_id oid;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
+ {
+ if (msg_ps->vb_ptr->ident_len > 3)
+ {
+ /* can offset ident_len and ident */
+ mn = snmp_expand_tree((struct mib_node*)&internet,
+ msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &oid);
+ }
+ else
+ {
+ /* can't offset ident_len -4, ident + 4 */
+ mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
+ }
+ }
+ else
+ {
+ mn = NULL;
+ }
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_oid = oid;
+
+ en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+ struct snmp_varbind *vb;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
+ mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
+
+ LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
+ vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
+ if (vb != NULL)
+ {
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
+ mn->get_value(&object_def, object_def.v_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ }
+ if (mn == NULL)
+ {
+ /* mn == NULL, noSuchName */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ snmp_ok_response(msg_ps);
+ }
+}
+
+/**
+ * Service an internal or external event for SNMP SET.
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ * @param msg_ps points to the assosicated message process state
+ */
+static void
+snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
+{
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
+
+ if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
+ {
+ struct mib_external_node *en;
+ struct snmp_name_ptr np;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+ np = msg_ps->ext_name_ptr;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
+ if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
+ en->set_test_q(request_id, &msg_ps->ext_object_def);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, np.ident_len, np.ident);
+ /* search failed, object id points to unknown object (nosuchname) */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
+ {
+ struct mib_external_node *en;
+
+ /* set_test() answer*/
+ en = msg_ps->ext_mib_node;
+
+ if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
+ {
+ if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
+ (en->set_test_a(request_id,&msg_ps->ext_object_def,
+ msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
+ {
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ en->set_test_pc(request_id,&msg_ps->ext_object_def);
+ /* bad value */
+ snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
+ }
+ }
+ else
+ {
+ en->set_test_pc(request_id,&msg_ps->ext_object_def);
+ /* object not available for set */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
+ {
+ struct mib_external_node *en;
+ struct snmp_name_ptr np;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+ np = msg_ps->ext_name_ptr;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
+ if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
+ en->set_value_q(request_id, &msg_ps->ext_object_def,
+ msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, np.ident_len, np.ident);
+ /* set_value failed, object has disappeared for some odd reason?? */
+ snmp_error_response(msg_ps,SNMP_ES_GENERROR);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
+ {
+ struct mib_external_node *en;
+
+ /** set_value_a() */
+ en = msg_ps->ext_mib_node;
+ en->set_value_a(request_id, &msg_ps->ext_object_def,
+ msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
+
+ /** @todo use set_value_pc() if toobig */
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
+ msg_ps->vb_idx += 1;
+ }
+
+ /* test all values before setting */
+ while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_name_ptr np;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ /** test object identifier for .iso.org.dod.internet prefix */
+ if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident))
+ {
+ mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &np);
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_name_ptr = np;
+
+ en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
+ mn->get_object_def(np.ident_len, np.ident, &object_def);
+ if (object_def.instance != MIB_OBJECT_NONE)
+ {
+ mn = mn;
+ }
+ else
+ {
+ /* search failed, object id points to unknown object (nosuchname) */
+ mn = NULL;
+ }
+ if (mn != NULL)
+ {
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
+
+ if (object_def.access & MIB_ACCESS_WRITE)
+ {
+ if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
+ (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
+ {
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ /* bad value */
+ snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
+ }
+ }
+ else
+ {
+ /* object not available for set */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ mn = NULL;
+ }
+ if (mn == NULL)
+ {
+ /* mn == NULL, noSuchName */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+
+ if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ msg_ps->vb_idx = 0;
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
+ }
+
+ /* set all values "atomically" (be as "atomic" as possible) */
+ while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_name_ptr np;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ /* skip iso prefix test, was done previously while settesting() */
+ mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &np);
+ /* check if object is still available
+ (e.g. external hot-plug thingy present?) */
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_name_ptr = np;
+
+ en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
+ mn->get_object_def(np.ident_len, np.ident, &object_def);
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
+ mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
+ msg_ps->vb_idx += 1;
+ }
+ }
+ }
+ if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ /* simply echo the input if we can set it
+ @todo do we need to return the actual value?
+ e.g. if value is silently modified or behaves sticky? */
+ msg_ps->outvb = msg_ps->invb;
+ msg_ps->invb.head = NULL;
+ msg_ps->invb.tail = NULL;
+ msg_ps->invb.count = 0;
+ snmp_ok_response(msg_ps);
+ }
+}
+
+
+/**
+ * Handle one internal or external event.
+ * Called for one async event. (recv external/private answer)
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ */
+void
+snmp_msg_event(u8_t request_id)
+{
+ struct snmp_msg_pstat *msg_ps;
+
+ if (request_id < SNMP_CONCURRENT_REQUESTS)
+ {
+ msg_ps = &msg_input_list[request_id];
+ if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
+ {
+ snmp_msg_getnext_event(request_id, msg_ps);
+ }
+ else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
+ {
+ snmp_msg_get_event(request_id, msg_ps);
+ }
+ else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
+ {
+ snmp_msg_set_event(request_id, msg_ps);
+ }
+ }
+}
+
+
+/* lwIP UDP receive callback function */
+static void
+snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ struct snmp_msg_pstat *msg_ps;
+ u8_t req_idx;
+ err_t err_ret;
+ u16_t payload_len = p->tot_len;
+ u16_t payload_ofs = 0;
+ u16_t varbind_ofs = 0;
+
+ /* suppress unused argument warning */
+ LWIP_UNUSED_ARG(arg);
+
+ /* traverse input message process list, look for SNMP_MSG_EMPTY */
+ msg_ps = &msg_input_list[0];
+ req_idx = 0;
+ while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
+ {
+ req_idx++;
+ msg_ps++;
+ }
+ if (req_idx == SNMP_CONCURRENT_REQUESTS)
+ {
+ /* exceeding number of concurrent requests */
+ pbuf_free(p);
+ return;
+ }
+
+ /* accepting request */
+ snmp_inc_snmpinpkts();
+ /* record used 'protocol control block' */
+ msg_ps->pcb = pcb;
+ /* source address (network order) */
+ msg_ps->sip = *addr;
+ /* source port (host order (lwIP oddity)) */
+ msg_ps->sp = port;
+
+ /* check total length, version, community, pdu type */
+ err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
+ /* Only accept requests and requests without error (be robust) */
+ /* Reject response and trap headers or error requests as input! */
+ if ((err_ret != ERR_OK) ||
+ ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
+ (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
+ (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
+ ((msg_ps->error_status != SNMP_ES_NOERROR) ||
+ (msg_ps->error_index != 0)) )
+ {
+ /* header check failed drop request silently, do not return error! */
+ pbuf_free(p);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
+ return;
+ }
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
+
+ /* Builds a list of variable bindings. Copy the varbinds from the pbuf
+ chain to glue them when these are divided over two or more pbuf's. */
+ err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
+ /* we've decoded the incoming message, release input msg now */
+ pbuf_free(p);
+ if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
+ {
+ /* varbind-list decode failed, or varbind list empty.
+ drop request silently, do not return error!
+ (errors are only returned for a specific varbind failure) */
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
+ return;
+ }
+
+ msg_ps->error_status = SNMP_ES_NOERROR;
+ msg_ps->error_index = 0;
+ /* find object for each variable binding */
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ /* first variable binding from list to inspect */
+ msg_ps->vb_idx = 0;
+
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
+
+ /* handle input event and as much objects as possible in one go */
+ snmp_msg_event(req_idx);
+}
+
+/**
+ * Checks and decodes incoming SNMP message header, logs header errors.
+ *
+ * @param p points to pbuf chain of SNMP message (UDP payload)
+ * @param ofs points to first octet of SNMP message
+ * @param pdu_len the length of the UDP payload
+ * @param ofs_ret returns the ofset of the variable bindings
+ * @param m_stat points to the current message request state return
+ * @return
+ * - ERR_OK SNMP header is sane and accepted
+ * - ERR_ARG SNMP header is either malformed or rejected
+ */
+static err_t
+snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
+{
+ err_t derr;
+ u16_t len, ofs_base;
+ u8_t len_octets;
+ u8_t type;
+ s32_t version;
+
+ ofs_base = ofs;
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) ||
+ (pdu_len != (1 + len_octets + len)) ||
+ (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (version) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ if (version != 0)
+ {
+ /* not version 1 */
+ snmp_inc_snmpinbadversions();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
+ {
+ /* can't decode or no octet string (community) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
+ if (derr != ERR_OK)
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ /* add zero terminator */
+ len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
+ m_stat->community[len] = 0;
+ m_stat->com_strlen = (u8_t)len;
+ if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
+ {
+ /** @todo: move this if we need to check more names */
+ snmp_inc_snmpinbadcommunitynames();
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if (derr != ERR_OK)
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ switch(type)
+ {
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
+ /* GetRequest PDU */
+ snmp_inc_snmpingetrequests();
+ derr = ERR_OK;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
+ /* GetNextRequest PDU */
+ snmp_inc_snmpingetnexts();
+ derr = ERR_OK;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
+ /* GetResponse PDU */
+ snmp_inc_snmpingetresponses();
+ derr = ERR_ARG;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
+ /* SetRequest PDU */
+ snmp_inc_snmpinsetrequests();
+ derr = ERR_OK;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
+ /* Trap PDU */
+ snmp_inc_snmpintraps();
+ derr = ERR_ARG;
+ break;
+ default:
+ snmp_inc_snmpinasnparseerrs();
+ derr = ERR_ARG;
+ break;
+ }
+ if (derr != ERR_OK)
+ {
+ /* unsupported input PDU for this agent (no parse error) */
+ return ERR_ARG;
+ }
+ m_stat->rt = type & 0x1F;
+ ofs += (1 + len_octets);
+ if (len != (pdu_len - (ofs - ofs_base)))
+ {
+ /* decoded PDU length does not equal actual payload length */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (request ID) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (error-status) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ /* must be noError (0) for incoming requests.
+ log errors for mib-2 completeness and for debug purposes */
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ switch (m_stat->error_status)
+ {
+ case SNMP_ES_TOOBIG:
+ snmp_inc_snmpintoobigs();
+ break;
+ case SNMP_ES_NOSUCHNAME:
+ snmp_inc_snmpinnosuchnames();
+ break;
+ case SNMP_ES_BADVALUE:
+ snmp_inc_snmpinbadvalues();
+ break;
+ case SNMP_ES_READONLY:
+ snmp_inc_snmpinreadonlys();
+ break;
+ case SNMP_ES_GENERROR:
+ snmp_inc_snmpingenerrs();
+ break;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (error-index) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ /* must be 0 for incoming requests.
+ decode anyway to catch bad integers (and dirty tricks) */
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ *ofs_ret = ofs;
+ return ERR_OK;
+}
+
+static err_t
+snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
+{
+ err_t derr;
+ u16_t len, vb_len;
+ u8_t len_octets;
+ u8_t type;
+
+ /* variable binding list */
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
+ if ((derr != ERR_OK) ||
+ (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets);
+
+ /* start with empty list */
+ m_stat->invb.count = 0;
+ m_stat->invb.head = NULL;
+ m_stat->invb.tail = NULL;
+
+ while (vb_len > 0)
+ {
+ struct snmp_obj_id oid, oid_value;
+ struct snmp_varbind *vb;
+
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) ||
+ (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
+ (len == 0) || (len > vb_len))
+ {
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets);
+ vb_len -= (1 + len_octets);
+
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
+ {
+ /* can't decode object name length */
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
+ if (derr != ERR_OK)
+ {
+ /* can't decode object name */
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ vb_len -= (1 + len_octets + len);
+
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if (derr != ERR_OK)
+ {
+ /* can't decode object value length */
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+
+ switch (type)
+ {
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
+ vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
+ if (vb != NULL)
+ {
+ s32_t *vptr = (s32_t*)vb->value;
+
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
+ vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
+ if (vb != NULL)
+ {
+ u32_t *vptr = (u32_t*)vb->value;
+
+ derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
+ LWIP_ASSERT("invalid length", len <= 0xff);
+ vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
+ if (vb != NULL)
+ {
+ derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
+ vb = snmp_varbind_alloc(&oid, type, 0);
+ if (vb != NULL)
+ {
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ derr = ERR_OK;
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
+ derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
+ if (derr == ERR_OK)
+ {
+ vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
+ if (vb != NULL)
+ {
+ u8_t i = oid_value.len;
+ s32_t *vptr = (s32_t*)vb->value;
+
+ while(i > 0)
+ {
+ i--;
+ vptr[i] = oid_value.id[i];
+ }
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ derr = ERR_OK;
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ }
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
+ if (len == 4)
+ {
+ /* must be exactly 4 octets! */
+ vb = snmp_varbind_alloc(&oid, type, 4);
+ if (vb != NULL)
+ {
+ derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ default:
+ derr = ERR_ARG;
+ break;
+ }
+ if (derr != ERR_OK)
+ {
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ vb_len -= (1 + len_octets + len);
+ }
+
+ if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
+ {
+ snmp_add_snmpintotalsetvars(m_stat->invb.count);
+ }
+ else
+ {
+ snmp_add_snmpintotalreqvars(m_stat->invb.count);
+ }
+
+ *ofs_ret = ofs;
+ return ERR_OK;
+}
+
+struct snmp_varbind*
+snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
+{
+ struct snmp_varbind *vb;
+
+ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
+ LWIP_ASSERT("vb != NULL",vb != NULL);
+ if (vb != NULL)
+ {
+ u8_t i;
+
+ vb->next = NULL;
+ vb->prev = NULL;
+ i = oid->len;
+ vb->ident_len = i;
+ if (i > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
+ /* allocate array of s32_t for our object identifier */
+ vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
+ LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL);
+ if (vb->ident == NULL)
+ {
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ return NULL;
+ }
+ while(i > 0)
+ {
+ i--;
+ vb->ident[i] = oid->id[i];
+ }
+ }
+ else
+ {
+ /* i == 0, pass zero length object identifier */
+ vb->ident = NULL;
+ }
+ vb->value_type = type;
+ vb->value_len = len;
+ if (len > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
+ /* allocate raw bytes for our object value */
+ vb->value = memp_malloc(MEMP_SNMP_VALUE);
+ LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
+ if (vb->value == NULL)
+ {
+ if (vb->ident != NULL)
+ {
+ memp_free(MEMP_SNMP_VALUE, vb->ident);
+ }
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* ASN1_NUL type, or zero length ASN1_OC_STR */
+ vb->value = NULL;
+ }
+ }
+ return vb;
+}
+
+void
+snmp_varbind_free(struct snmp_varbind *vb)
+{
+ if (vb->value != NULL )
+ {
+ memp_free(MEMP_SNMP_VALUE, vb->value);
+ }
+ if (vb->ident != NULL )
+ {
+ memp_free(MEMP_SNMP_VALUE, vb->ident);
+ }
+ memp_free(MEMP_SNMP_VARBIND, vb);
+}
+
+void
+snmp_varbind_list_free(struct snmp_varbind_root *root)
+{
+ struct snmp_varbind *vb, *prev;
+
+ vb = root->tail;
+ while ( vb != NULL )
+ {
+ prev = vb->prev;
+ snmp_varbind_free(vb);
+ vb = prev;
+ }
+ root->count = 0;
+ root->head = NULL;
+ root->tail = NULL;
+}
+
+void
+snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
+{
+ if (root->count == 0)
+ {
+ /* add first varbind to list */
+ root->head = vb;
+ root->tail = vb;
+ }
+ else
+ {
+ /* add nth varbind to list tail */
+ root->tail->next = vb;
+ vb->prev = root->tail;
+ root->tail = vb;
+ }
+ root->count += 1;
+}
+
+struct snmp_varbind*
+snmp_varbind_tail_remove(struct snmp_varbind_root *root)
+{
+ struct snmp_varbind* vb;
+
+ if (root->count > 0)
+ {
+ /* remove tail varbind */
+ vb = root->tail;
+ root->tail = vb->prev;
+ vb->prev->next = NULL;
+ root->count -= 1;
+ }
+ else
+ {
+ /* nothing to remove */
+ vb = NULL;
+ }
+ return vb;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/core/lwip/src/core/snmp/msg_out.c b/core/lwip/src/core/snmp/msg_out.c
new file mode 100644
index 00000000..4778bee6
--- /dev/null
+++ b/core/lwip/src/core/snmp/msg_out.c
@@ -0,0 +1,681 @@
+/**
+ * @file
+ * SNMP output message processing (RFC1157).
+ *
+ * Output responses and traps are build in two passes:
+ *
+ * Pass 0: iterate over the output message backwards to determine encoding lengths
+ * Pass 1: the actual forward encoding of internal form into ASN1
+ *
+ * The single-pass encoding method described by Comer & Stevens
+ * requires extra buffer space and copying for reversal of the packet.
+ * The buffer requirement can be prohibitively large for big payloads
+ * (>= 484) therefore we use the two encoding passes.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_msg.h"
+
+struct snmp_trap_dst
+{
+ /* destination IP address in network order */
+ ip_addr_t dip;
+ /* set to 0 when disabled, >0 when enabled */
+ u8_t enable;
+};
+struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
+
+/** TRAP message structure */
+struct snmp_msg_trap trap_msg;
+
+static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len);
+static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len);
+static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root);
+
+static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p);
+static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p);
+static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs);
+
+/**
+ * Sets enable switch for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param enable switch if 0 destination is disabled >0 enabled.
+ */
+void
+snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
+{
+ if (dst_idx < SNMP_TRAP_DESTINATIONS)
+ {
+ trap_dst[dst_idx].enable = enable;
+ }
+}
+
+/**
+ * Sets IPv4 address for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param dst IPv4 address in host order.
+ */
+void
+snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst)
+{
+ if (dst_idx < SNMP_TRAP_DESTINATIONS)
+ {
+ ip_addr_set(&trap_dst[dst_idx].dip, dst);
+ }
+}
+
+/**
+ * Sends a 'getresponse' message to the request originator.
+ *
+ * @param m_stat points to the current message request state source
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the caller is responsible for filling in outvb in the m_stat
+ * and provide error-status and index (except for tooBig errors) ...
+ */
+err_t
+snmp_send_response(struct snmp_msg_pstat *m_stat)
+{
+ struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0};
+ struct pbuf *p;
+ u16_t tot_len;
+ err_t err;
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_varbind_list_sum(&m_stat->outvb);
+ tot_len = snmp_resp_header_sum(m_stat, tot_len);
+
+ /* try allocating pbuf(s) for complete response */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ if (p == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n"));
+
+ /* can't construct reply, return error-status tooBig */
+ m_stat->error_status = SNMP_ES_TOOBIG;
+ m_stat->error_index = 0;
+ /* pass 0, recalculate lengths, for empty varbind-list */
+ tot_len = snmp_varbind_list_sum(&emptyvb);
+ tot_len = snmp_resp_header_sum(m_stat, tot_len);
+ /* retry allocation once for header and empty varbind-list */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ }
+ if (p != NULL)
+ {
+ /* first pbuf alloc try or retry alloc success */
+ u16_t ofs;
+
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n"));
+
+ /* pass 1, size error, encode packet ino the pbuf(s) */
+ ofs = snmp_resp_header_enc(m_stat, p);
+ if (m_stat->error_status == SNMP_ES_TOOBIG)
+ {
+ snmp_varbind_list_enc(&emptyvb, p, ofs);
+ }
+ else
+ {
+ snmp_varbind_list_enc(&m_stat->outvb, p, ofs);
+ }
+
+ switch (m_stat->error_status)
+ {
+ case SNMP_ES_TOOBIG:
+ snmp_inc_snmpouttoobigs();
+ break;
+ case SNMP_ES_NOSUCHNAME:
+ snmp_inc_snmpoutnosuchnames();
+ break;
+ case SNMP_ES_BADVALUE:
+ snmp_inc_snmpoutbadvalues();
+ break;
+ case SNMP_ES_GENERROR:
+ snmp_inc_snmpoutgenerrs();
+ break;
+ }
+ snmp_inc_snmpoutgetresponses();
+ snmp_inc_snmpoutpkts();
+
+ /** @todo do we need separate rx and tx pcbs for threaded case? */
+ /** connect to the originating source */
+ udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp);
+ err = udp_send(m_stat->pcb, p);
+ if (err == ERR_MEM)
+ {
+ /** @todo release some memory, retry and return tooBig? tooMuchHassle? */
+ err = ERR_MEM;
+ }
+ else
+ {
+ err = ERR_OK;
+ }
+ /** disassociate remote address and port with this pcb */
+ udp_disconnect(m_stat->pcb);
+
+ pbuf_free(p);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n"));
+ return err;
+ }
+ else
+ {
+ /* first pbuf alloc try or retry alloc failed
+ very low on memory, couldn't return tooBig */
+ return ERR_MEM;
+ }
+}
+
+
+/**
+ * Sends an generic or enterprise specific trap message.
+ *
+ * @param generic_trap is the trap code
+ * @param eoid points to enterprise object identifier
+ * @param specific_trap used for enterprise traps when generic_trap == 6
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the caller is responsible for filling in outvb in the trap_msg
+ * @note the use of the enterpise identifier field
+ * is per RFC1215.
+ * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
+ * and .iso.org.dod.internet.private.enterprises.yourenterprise
+ * (sysObjectID) for specific traps.
+ */
+err_t
+snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap)
+{
+ struct snmp_trap_dst *td;
+ struct netif *dst_if;
+ ip_addr_t dst_ip;
+ struct pbuf *p;
+ u16_t i,tot_len;
+
+ for (i=0, td = &trap_dst[0]; i<SNMP_TRAP_DESTINATIONS; i++, td++)
+ {
+ if ((td->enable != 0) && !ip_addr_isany(&td->dip))
+ {
+ /* network order trap destination */
+ ip_addr_copy(trap_msg.dip, td->dip);
+ /* lookup current source address for this dst */
+ dst_if = ip_route(&td->dip);
+ ip_addr_copy(dst_ip, dst_if->ip_addr);
+ /* @todo: what about IPv6? */
+ trap_msg.sip_raw[0] = ip4_addr1(&dst_ip);
+ trap_msg.sip_raw[1] = ip4_addr2(&dst_ip);
+ trap_msg.sip_raw[2] = ip4_addr3(&dst_ip);
+ trap_msg.sip_raw[3] = ip4_addr4(&dst_ip);
+ trap_msg.gen_trap = generic_trap;
+ trap_msg.spc_trap = specific_trap;
+ if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC)
+ {
+ /* enterprise-Specific trap */
+ trap_msg.enterprise = eoid;
+ }
+ else
+ {
+ /* generic (MIB-II) trap */
+ snmp_get_snmpgrpid_ptr(&trap_msg.enterprise);
+ }
+ snmp_get_sysuptime(&trap_msg.ts);
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_varbind_list_sum(&trap_msg.outvb);
+ tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+ /* allocate pbuf(s) */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ if (p != NULL)
+ {
+ u16_t ofs;
+
+ /* pass 1, encode packet ino the pbuf(s) */
+ ofs = snmp_trap_header_enc(&trap_msg, p);
+ snmp_varbind_list_enc(&trap_msg.outvb, p, ofs);
+
+ snmp_inc_snmpouttraps();
+ snmp_inc_snmpoutpkts();
+
+ /** send to the TRAP destination */
+ udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT);
+
+ pbuf_free(p);
+ }
+ else
+ {
+ return ERR_MEM;
+ }
+ }
+ }
+ return ERR_OK;
+}
+
+void
+snmp_coldstart_trap(void)
+{
+ trap_msg.outvb.head = NULL;
+ trap_msg.outvb.tail = NULL;
+ trap_msg.outvb.count = 0;
+ snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0);
+}
+
+void
+snmp_authfail_trap(void)
+{
+ u8_t enable;
+ snmp_get_snmpenableauthentraps(&enable);
+ if (enable == 1)
+ {
+ trap_msg.outvb.head = NULL;
+ trap_msg.outvb.tail = NULL;
+ trap_msg.outvb.count = 0;
+ snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0);
+ }
+}
+
+/**
+ * Sums response header field lengths from tail to head and
+ * returns resp_header_lengths for second encoding pass.
+ *
+ * @param vb_len varbind-list length
+ * @param rhl points to returned header lengths
+ * @return the required lenght for encoding the response header
+ */
+static u16_t
+snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len)
+{
+ u16_t tot_len;
+ struct snmp_resp_header_lengths *rhl;
+
+ rhl = &m_stat->rhl;
+ tot_len = vb_len;
+ snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen);
+ snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen);
+ tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen;
+
+ snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen);
+ snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen);
+ tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen;
+
+ snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen);
+ snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen);
+ tot_len += 1 + rhl->ridlenlen + rhl->ridlen;
+
+ rhl->pdulen = tot_len;
+ snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen);
+ tot_len += 1 + rhl->pdulenlen;
+
+ rhl->comlen = m_stat->com_strlen;
+ snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen);
+ tot_len += 1 + rhl->comlenlen + rhl->comlen;
+
+ snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen);
+ snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen);
+ tot_len += 1 + rhl->verlen + rhl->verlenlen;
+
+ rhl->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen);
+ tot_len += 1 + rhl->seqlenlen;
+
+ return tot_len;
+}
+
+/**
+ * Sums trap header field lengths from tail to head and
+ * returns trap_header_lengths for second encoding pass.
+ *
+ * @param vb_len varbind-list length
+ * @param thl points to returned header lengths
+ * @return the required lenght for encoding the trap header
+ */
+static u16_t
+snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len)
+{
+ u16_t tot_len;
+ struct snmp_trap_header_lengths *thl;
+
+ thl = &m_trap->thl;
+ tot_len = vb_len;
+
+ snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen);
+ snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen);
+ tot_len += 1 + thl->tslen + thl->tslenlen;
+
+ snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen);
+ snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen);
+ tot_len += 1 + thl->strplen + thl->strplenlen;
+
+ snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen);
+ snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen);
+ tot_len += 1 + thl->gtrplen + thl->gtrplenlen;
+
+ thl->aaddrlen = 4;
+ snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen);
+ tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen;
+
+ snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen);
+ snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen);
+ tot_len += 1 + thl->eidlen + thl->eidlenlen;
+
+ thl->pdulen = tot_len;
+ snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen);
+ tot_len += 1 + thl->pdulenlen;
+
+ thl->comlen = sizeof(snmp_publiccommunity) - 1;
+ snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen);
+ tot_len += 1 + thl->comlenlen + thl->comlen;
+
+ snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen);
+ snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen);
+ tot_len += 1 + thl->verlen + thl->verlenlen;
+
+ thl->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen);
+ tot_len += 1 + thl->seqlenlen;
+
+ return tot_len;
+}
+
+/**
+ * Sums varbind lengths from tail to head and
+ * annotates lengths in varbind for second encoding pass.
+ *
+ * @param root points to the root of the variable binding list
+ * @return the required lenght for encoding the variable bindings
+ */
+static u16_t
+snmp_varbind_list_sum(struct snmp_varbind_root *root)
+{
+ struct snmp_varbind *vb;
+ u32_t *uint_ptr;
+ s32_t *sint_ptr;
+ u16_t tot_len;
+
+ tot_len = 0;
+ vb = root->tail;
+ while ( vb != NULL )
+ {
+ /* encoded value lenght depends on type */
+ switch (vb->value_type)
+ {
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen);
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
+ uint_ptr = (u32_t*)vb->value;
+ snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen);
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
+ vb->vlen = vb->value_len;
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen);
+ break;
+ default:
+ /* unsupported type */
+ vb->vlen = 0;
+ break;
+ };
+ /* encoding length of value length field */
+ snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen);
+ snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen);
+ snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen);
+
+ vb->seqlen = 1 + vb->vlenlen + vb->vlen;
+ vb->seqlen += 1 + vb->olenlen + vb->olen;
+ snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen);
+
+ /* varbind seq */
+ tot_len += 1 + vb->seqlenlen + vb->seqlen;
+
+ vb = vb->prev;
+ }
+
+ /* varbind-list seq */
+ root->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen);
+ tot_len += 1 + root->seqlenlen;
+
+ return tot_len;
+}
+
+/**
+ * Encodes response header from head to tail.
+ */
+static u16_t
+snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p)
+{
+ u16_t ofs;
+
+ ofs = 0;
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen);
+ ofs += m_stat->rhl.seqlenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen);
+ ofs += m_stat->rhl.verlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version);
+ ofs += m_stat->rhl.verlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen);
+ ofs += m_stat->rhl.comlenlen;
+ snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community);
+ ofs += m_stat->rhl.comlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen);
+ ofs += m_stat->rhl.pdulenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen);
+ ofs += m_stat->rhl.ridlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid);
+ ofs += m_stat->rhl.ridlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen);
+ ofs += m_stat->rhl.errstatlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status);
+ ofs += m_stat->rhl.errstatlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen);
+ ofs += m_stat->rhl.erridxlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index);
+ ofs += m_stat->rhl.erridxlen;
+
+ return ofs;
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static u16_t
+snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p)
+{
+ u16_t ofs;
+
+ ofs = 0;
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen);
+ ofs += m_trap->thl.seqlenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen);
+ ofs += m_trap->thl.verlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version);
+ ofs += m_trap->thl.verlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen);
+ ofs += m_trap->thl.comlenlen;
+ snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]);
+ ofs += m_trap->thl.comlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen);
+ ofs += m_trap->thl.pdulenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen);
+ ofs += m_trap->thl.eidlenlen;
+ snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]);
+ ofs += m_trap->thl.eidlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen);
+ ofs += m_trap->thl.aaddrlenlen;
+ snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]);
+ ofs += m_trap->thl.aaddrlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen);
+ ofs += m_trap->thl.gtrplenlen;
+ snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap);
+ ofs += m_trap->thl.gtrplen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen);
+ ofs += m_trap->thl.strplenlen;
+ snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap);
+ ofs += m_trap->thl.strplen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen);
+ ofs += m_trap->thl.tslenlen;
+ snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts);
+ ofs += m_trap->thl.tslen;
+
+ return ofs;
+}
+
+/**
+ * Encodes varbind list from head to tail.
+ */
+static u16_t
+snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs)
+{
+ struct snmp_varbind *vb;
+ s32_t *sint_ptr;
+ u32_t *uint_ptr;
+ u8_t *raw_ptr;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, root->seqlen);
+ ofs += root->seqlenlen;
+
+ vb = root->head;
+ while ( vb != NULL )
+ {
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, vb->seqlen);
+ ofs += vb->seqlenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, vb->olen);
+ ofs += vb->olenlen;
+ snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]);
+ ofs += vb->olen;
+
+ snmp_asn1_enc_type(p, ofs, vb->value_type);
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, vb->vlen);
+ ofs += vb->vlenlen;
+
+ switch (vb->value_type)
+ {
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr);
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
+ uint_ptr = (u32_t*)vb->value;
+ snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr);
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
+ raw_ptr = (u8_t*)vb->value;
+ snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr);
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr);
+ break;
+ default:
+ /* unsupported type */
+ break;
+ };
+ ofs += vb->vlen;
+ vb = vb->next;
+ }
+ return ofs;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/core/lwip/src/core/stats.c b/core/lwip/src/core/stats.c
new file mode 100644
index 00000000..69f97d41
--- /dev/null
+++ b/core/lwip/src/core/stats.c
@@ -0,0 +1,176 @@
+/**
+ * @file
+ * Statistics module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+struct stats_ lwip_stats;
+
+void stats_init(void)
+{
+#ifdef LWIP_DEBUG
+#if MEMP_STATS
+ const char * memp_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) desc,
+#include "lwip/memp_std.h"
+ };
+ int i;
+ for (i = 0; i < MEMP_MAX; i++) {
+ lwip_stats.memp[i].name = memp_names[i];
+ }
+#endif /* MEMP_STATS */
+#if MEM_STATS
+ lwip_stats.mem.name = "MEM";
+#endif /* MEM_STATS */
+#endif /* LWIP_DEBUG */
+}
+
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, char *name)
+{
+ LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
+ LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
+ LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
+ LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
+ LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
+ LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
+}
+
+#if IGMP_STATS
+void
+stats_display_igmp(struct stats_igmp *igmp)
+{
+ LWIP_PLATFORM_DIAG(("\nIGMP\n\t"));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+ LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+ LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group));
+ LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general));
+ LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+ LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+ LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+ LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report));
+}
+#endif /* IGMP_STATS */
+
+#if MEM_STATS || MEMP_STATS
+void
+stats_display_mem(struct stats_mem *mem, char *name)
+{
+ LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
+ LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail));
+ LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used));
+ LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max));
+ LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
+}
+
+#if MEMP_STATS
+void
+stats_display_memp(struct stats_mem *mem, int index)
+{
+ char * memp_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) desc,
+#include "lwip/memp_std.h"
+ };
+ if(index < MEMP_MAX) {
+ stats_display_mem(mem, memp_names[index]);
+ }
+}
+#endif /* MEMP_STATS */
+#endif /* MEM_STATS || MEMP_STATS */
+
+#if SYS_STATS
+void
+stats_display_sys(struct stats_sys *sys)
+{
+ LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
+ LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
+ LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
+ LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
+ LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
+ LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
+ LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
+ LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
+ LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
+ LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err));
+}
+#endif /* SYS_STATS */
+
+void
+stats_display(void)
+{
+ s16_t i;
+
+ LINK_STATS_DISPLAY();
+ ETHARP_STATS_DISPLAY();
+ IPFRAG_STATS_DISPLAY();
+ IP_STATS_DISPLAY();
+ IGMP_STATS_DISPLAY();
+ ICMP_STATS_DISPLAY();
+ UDP_STATS_DISPLAY();
+ TCP_STATS_DISPLAY();
+ MEM_STATS_DISPLAY();
+ for (i = 0; i < MEMP_MAX; i++) {
+ MEMP_STATS_DISPLAY(i);
+ }
+ SYS_STATS_DISPLAY();
+}
+#endif /* LWIP_STATS_DISPLAY */
+
+#endif /* LWIP_STATS */
+
diff --git a/core/lwip/src/core/sys.c b/core/lwip/src/core/sys.c
new file mode 100644
index 00000000..d3a77deb
--- /dev/null
+++ b/core/lwip/src/core/sys.c
@@ -0,0 +1,66 @@
+/**
+ * @file
+ * lwIP Operating System abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/sys.h"
+
+/* Most of the functions defined in sys.h must be implemented in the
+ * architecture-dependent file sys_arch.c */
+
+#if !NO_SYS
+
+/**
+ * Sleep for some ms. Timeouts are NOT processed while sleeping.
+ *
+ * @param ms number of milliseconds to sleep
+ */
+void
+sys_msleep(u32_t ms)
+{
+ if (ms > 0) {
+ sys_sem_t delaysem;
+ err_t err = sys_sem_new(&delaysem, 0);
+ if (err == ERR_OK) {
+ sys_arch_sem_wait(&delaysem, ms);
+ sys_sem_free(&delaysem);
+ }
+ }
+}
+
+#endif /* !NO_SYS */
diff --git a/core/lwip/src/core/tcp.c b/core/lwip/src/core/tcp.c
new file mode 100644
index 00000000..c629bc4e
--- /dev/null
+++ b/core/lwip/src/core/tcp.c
@@ -0,0 +1,1635 @@
+/**
+ * @file
+ * Transmission Control Protocol for IP
+ *
+ * This file contains common functions for the TCP implementation, such as functinos
+ * for manipulating the data structures and the TCP timer functions. TCP functions
+ * related to input and output is found in tcp_in.c and tcp_out.c respectively.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/snmp.h"
+#include "lwip/tcp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+const char * const tcp_state_str[] = {
+ "CLOSED",
+ "LISTEN",
+ "SYN_SENT",
+ "SYN_RCVD",
+ "ESTABLISHED",
+ "FIN_WAIT_1",
+ "FIN_WAIT_2",
+ "CLOSE_WAIT",
+ "CLOSING",
+ "LAST_ACK",
+ "TIME_WAIT"
+};
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+u32_t tcp_ticks;
+const u8_t tcp_backoff[13] =
+ { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+ /* Times per slowtmr hits */
+const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+/* The TCP PCB lists. */
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+#define NUM_TCP_PCB_LISTS 4
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+ &tcp_active_pcbs, &tcp_tw_pcbs};
+
+/** Only used for temporary storage. */
+struct tcp_pcb *tcp_tmp_pcb;
+
+/** Timer counter to handle calling slow-timer from tcp_tmr() */
+static u8_t tcp_timer;
+static u16_t tcp_new_port(void);
+
+/**
+ * Called periodically to dispatch TCP timers.
+ *
+ */
+void
+tcp_tmr(void)
+{
+ /* Call tcp_fasttmr() every 250 ms */
+ tcp_fasttmr();
+
+ if (++tcp_timer & 1) {
+ /* Call tcp_tmr() every 500 ms, i.e., every other timer
+ tcp_tmr() is called. */
+ tcp_slowtmr();
+ }
+}
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
+{
+ err_t err;
+
+ if (rst_on_unacked_data && (pcb->state != LISTEN)) {
+ if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
+ /* Not all data received by application, send RST to tell the remote
+ side about this. */
+ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+ /* don't call tcp_abort here: we must not deallocate the pcb since
+ that might not be expected when calling tcp_close */
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+
+ tcp_pcb_purge(pcb);
+
+ /* TODO: to which state do we move now? */
+
+ /* move to TIME_WAIT since we close actively */
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+
+ return ERR_OK;
+ }
+ }
+
+ switch (pcb->state) {
+ case CLOSED:
+ /* Closing a pcb in the CLOSED state might seem erroneous,
+ * however, it is in this state once allocated and as yet unused
+ * and the user needs some way to free it should the need arise.
+ * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+ * or for a pcb that has been used and then entered the CLOSED state
+ * is erroneous, but this should never happen as the pcb has in those cases
+ * been freed, and so any remaining handles are bogus. */
+ err = ERR_OK;
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+ pcb = NULL;
+ break;
+ case LISTEN:
+ err = ERR_OK;
+ tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
+ memp_free(MEMP_TCP_PCB_LISTEN, pcb);
+ pcb = NULL;
+ break;
+ case SYN_SENT:
+ err = ERR_OK;
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ pcb = NULL;
+ snmp_inc_tcpattemptfails();
+ break;
+ case SYN_RCVD:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpattemptfails();
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case ESTABLISHED:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpestabresets();
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case CLOSE_WAIT:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpestabresets();
+ pcb->state = LAST_ACK;
+ }
+ break;
+ default:
+ /* Has already been closed, do nothing. */
+ err = ERR_OK;
+ pcb = NULL;
+ break;
+ }
+
+ if (pcb != NULL && err == ERR_OK) {
+ /* To ensure all data has been sent when tcp_close returns, we have
+ to make sure tcp_output doesn't fail.
+ Since we don't really have to ensure all data has been sent when tcp_close
+ returns (unsent data is sent from tcp timer functions, also), we don't care
+ for the return value of tcp_output for now. */
+ /* @todo: When implementing SO_LINGER, this must be changed somehow:
+ If SOF_LINGER is set, the data should be sent and acked before close returns.
+ This can only be valid for sequential APIs, not for the raw API. */
+ tcp_output(pcb);
+ }
+ return err;
+}
+
+/**
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+#if TCP_DEBUG
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+
+ if (pcb->state != LISTEN) {
+ /* Set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ /* ... and close */
+ return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB!
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ * another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+ if (pcb->state == LISTEN) {
+ return ERR_CONN;
+ }
+ if (shut_rx) {
+ /* shut down the receive side: free buffered data... */
+ if (pcb->refused_data != NULL) {
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ /* ... and set a flag not to receive any more data */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ if (shut_tx) {
+ /* This can't happen twice since if it succeeds, the pcb's state is changed.
+ Only close in these states as the others directly deallocate the PCB */
+ switch (pcb->state) {
+ case SYN_RCVD:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ return tcp_close_shutdown(pcb, 0);
+ default:
+ /* don't shut down other states */
+ break;
+ }
+ }
+ /* @todo: return another err_t if not in correct state or already shut? */
+ return ERR_OK;
+}
+
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host. Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+ u32_t seqno, ackno;
+ u16_t remote_port, local_port;
+ ip_addr_t remote_ip, local_ip;
+#if LWIP_CALLBACK_API
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+ void *errf_arg;
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+ pcb->state != LISTEN);
+ /* Figure out on which TCP PCB list we are, and remove us. If we
+ are in an active state, call the receive function associated with
+ the PCB with a NULL argument, and send an RST to the remote end. */
+ if (pcb->state == TIME_WAIT) {
+ tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ seqno = pcb->snd_nxt;
+ ackno = pcb->rcv_nxt;
+ ip_addr_copy(local_ip, pcb->local_ip);
+ ip_addr_copy(remote_ip, pcb->remote_ip);
+ local_port = pcb->local_port;
+ remote_port = pcb->remote_port;
+#if LWIP_CALLBACK_API
+ errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+ errf_arg = pcb->callback_arg;
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ if (pcb->unacked != NULL) {
+ tcp_segs_free(pcb->unacked);
+ }
+ if (pcb->unsent != NULL) {
+ tcp_segs_free(pcb->unsent);
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ tcp_segs_free(pcb->ooseq);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+ memp_free(MEMP_TCP_PCB, pcb);
+ TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
+ if (reset) {
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+ tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
+ }
+ }
+}
+
+/**
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+ tcp_abandon(pcb, 1);
+}
+
+/**
+ * Binds the connection to a local portnumber and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ * already bound!)
+ * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind
+ * to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ * ERR_VAL if bind failed because the PCB is not in a valid state
+ * ERR_OK if bound
+ */
+err_t
+tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ int i;
+ int max_pcb_list = NUM_TCP_PCB_LISTS;
+ struct tcp_pcb *cpcb;
+
+ LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+#if SO_REUSE
+ /* Unless the REUSEADDR flag is set,
+ we have to check the pcbs in TIME-WAIT state, also.
+ We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+ packets using both local and remote IP addresses and ports to distinguish.
+ */
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+ }
+#endif /* SO_REUSE */
+
+ if (port == 0) {
+ port = tcp_new_port();
+ }
+
+ /* Check if the address already is in use (on all lists) */
+ for (i = 0; i < max_pcb_list; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if (cpcb->local_port == port) {
+#if SO_REUSE
+ /* Omit checking for the same port if both pcbs have REUSEADDR set.
+ For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+ tcp_connect. */
+ if (((pcb->so_options & SOF_REUSEADDR) == 0) ||
+ ((cpcb->so_options & SOF_REUSEADDR) == 0))
+#endif /* SO_REUSE */
+ {
+ if (ip_addr_isany(&(cpcb->local_ip)) ||
+ ip_addr_isany(ipaddr) ||
+ ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+ return ERR_USE;
+ }
+ }
+ }
+ }
+ }
+
+ if (!ip_addr_isany(ipaddr)) {
+ pcb->local_ip = *ipaddr;
+ }
+ pcb->local_port = port;
+ TCP_REG(&tcp_bound_pcbs, pcb);
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+ return ERR_OK;
+}
+#if LWIP_CALLBACK_API
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+static err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(err);
+
+ return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ * called like this:
+ * tpcb = tcp_listen(tpcb);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+ struct tcp_pcb_listen *lpcb;
+
+ LWIP_UNUSED_ARG(backlog);
+ LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
+
+ /* already listening? */
+ if (pcb->state == LISTEN) {
+ return pcb;
+ }
+#if SO_REUSE
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+ is declared (listen-/connection-pcb), we have to make sure now that
+ this port is only used once for every local IP. */
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if (lpcb->local_port == pcb->local_port) {
+ if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
+ /* this address/port is already used */
+ return NULL;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
+ if (lpcb == NULL) {
+ return NULL;
+ }
+ lpcb->callback_arg = pcb->callback_arg;
+ lpcb->local_port = pcb->local_port;
+ lpcb->state = LISTEN;
+ lpcb->prio = pcb->prio;
+ lpcb->so_options = pcb->so_options;
+ lpcb->so_options |= SOF_ACCEPTCONN;
+ lpcb->ttl = pcb->ttl;
+ lpcb->tos = pcb->tos;
+ ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+#if LWIP_CALLBACK_API
+ lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+ lpcb->accepts_pending = 0;
+ lpcb->backlog = (backlog ? backlog : 1);
+#endif /* TCP_LISTEN_BACKLOG */
+ TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+ return (struct tcp_pcb *)lpcb;
+}
+
+/**
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+ u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+ if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
+ /* we can advertise more window */
+ pcb->rcv_ann_wnd = pcb->rcv_wnd;
+ return new_right_edge - pcb->rcv_ann_right_edge;
+ } else {
+ if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+ /* Can happen due to other end sending out of advertised window,
+ * but within actual available (but not yet advertised) window */
+ pcb->rcv_ann_wnd = 0;
+ } else {
+ /* keep the right edge of window constant */
+ u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+ LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+ pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
+ }
+ return 0;
+ }
+}
+
+/**
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+ int wnd_inflation;
+
+ LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n",
+ len <= 0xffff - pcb->rcv_wnd );
+
+ pcb->rcv_wnd += len;
+ if (pcb->rcv_wnd > TCP_WND) {
+ pcb->rcv_wnd = TCP_WND;
+ }
+
+ wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+ /* If the change in the right edge of window is significant (default
+ * watermark is TCP_WND/4), then send an explicit update now.
+ * Otherwise wait for a packet to be sent in the normal course of
+ * events (or more window to be available later) */
+ if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ }
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
+ len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
+}
+
+/**
+ * A nastly hack featuring 'goto' statements that allocates a
+ * new TCP local port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static u16_t
+tcp_new_port(void)
+{
+ int i;
+ struct tcp_pcb *pcb;
+#ifndef TCP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+ "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define TCP_LOCAL_PORT_RANGE_START 0xc000
+#define TCP_LOCAL_PORT_RANGE_END 0xffff
+#endif
+ static u16_t port = TCP_LOCAL_PORT_RANGE_START;
+
+ again:
+ if (port++ >= TCP_LOCAL_PORT_RANGE_END) {
+ port = TCP_LOCAL_PORT_RANGE_START;
+ }
+ /* Check all PCB lists. */
+ for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+ for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+ if (pcb->local_port == port) {
+ goto again;
+ }
+ }
+ }
+ return port;
+}
+
+/**
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *
+ * @param pcb the tcp_pcb used to establish the connection
+ * @param ipaddr the remote ip address to connect to
+ * @param port the remote tcp port to connect to
+ * @param connected callback function to call when connected (or on error)
+ * @return ERR_VAL if invalid arguments are given
+ * ERR_OK if connect request has been sent
+ * other err_t values if connect request couldn't be sent
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
+ tcp_connected_fn connected)
+{
+ err_t ret;
+ u32_t iss;
+ u16_t old_local_port;
+
+ LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+ if (ipaddr != NULL) {
+ pcb->remote_ip = *ipaddr;
+ } else {
+ return ERR_VAL;
+ }
+ pcb->remote_port = port;
+
+ /* check if we have a route to the remote host */
+ if (ip_addr_isany(&(pcb->local_ip))) {
+ /* no local IP address set, yet. */
+ struct netif *netif = ip_route(&(pcb->remote_ip));
+ if (netif == NULL) {
+ /* Don't even try to send a SYN packet if we have no route
+ since that will fail. */
+ return ERR_RTE;
+ }
+ /* Use the netif's IP address as local address. */
+ ip_addr_copy(pcb->local_ip, netif->ip_addr);
+ }
+
+ old_local_port = pcb->local_port;
+ if (pcb->local_port == 0) {
+ pcb->local_port = tcp_new_port();
+ }
+#if SO_REUSE
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+ now that the 5-tuple is unique. */
+ struct tcp_pcb *cpcb;
+ int i;
+ /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+ for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if ((cpcb->local_port == pcb->local_port) &&
+ (cpcb->remote_port == port) &&
+ ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
+ ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
+ /* linux returns EISCONN here, but ERR_USE should be OK for us */
+ return ERR_USE;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ iss = tcp_next_iss();
+ pcb->rcv_nxt = 0;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss - 1;
+ pcb->snd_lbb = iss - 1;
+ pcb->rcv_wnd = TCP_WND;
+ pcb->rcv_ann_wnd = TCP_WND;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->snd_wnd = TCP_WND;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+ pcb->cwnd = 1;
+ pcb->ssthresh = pcb->mss * 10;
+#if LWIP_CALLBACK_API
+ pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+ /* Send a SYN together with the MSS option. */
+ ret = tcp_enqueue_flags(pcb, TCP_SYN);
+ if (ret == ERR_OK) {
+ /* SYN segment was enqueued, changed the pcbs state now */
+ pcb->state = SYN_SENT;
+ if (old_local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ TCP_REG(&tcp_active_pcbs, pcb);
+ snmp_inc_tcpactiveopens();
+
+ tcp_output(pcb);
+ }
+ return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+ struct tcp_pcb *pcb, *prev;
+ u16_t eff_wnd;
+ u8_t pcb_remove; /* flag if a PCB should be removed */
+ u8_t pcb_reset; /* flag if a RST should be sent when removing */
+ err_t err;
+
+ err = ERR_OK;
+
+ ++tcp_ticks;
+
+ /* Steps through all of the active PCBs. */
+ prev = NULL;
+ pcb = tcp_active_pcbs;
+ if (pcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+ }
+ while (pcb != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
+
+ pcb_remove = 0;
+ pcb_reset = 0;
+
+ if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+ }
+ else if (pcb->nrtx == TCP_MAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+ } else {
+ if (pcb->persist_backoff > 0) {
+ /* If snd_wnd is zero, use persist timer to send 1 byte probes
+ * instead of using the standard retransmission mechanism. */
+ pcb->persist_cnt++;
+ if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
+ pcb->persist_cnt = 0;
+ if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+ pcb->persist_backoff++;
+ }
+ tcp_zero_window_probe(pcb);
+ }
+ } else {
+ /* Increase the retransmission timer if it is running */
+ if(pcb->rtime >= 0)
+ ++pcb->rtime;
+
+ if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+ /* Time for a retransmission. */
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+ " pcb->rto %"S16_F"\n",
+ pcb->rtime, pcb->rto));
+
+ /* Double retransmission time-out unless we are trying to
+ * connect to somebody (i.e., we are in SYN_SENT). */
+ if (pcb->state != SYN_SENT) {
+ pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
+ }
+
+ /* Reset the retransmission timer. */
+ pcb->rtime = 0;
+
+ /* Reduce congestion window and ssthresh. */
+ eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+ pcb->ssthresh = eff_wnd >> 1;
+ if (pcb->ssthresh < (pcb->mss << 1)) {
+ pcb->ssthresh = (pcb->mss << 1);
+ }
+ pcb->cwnd = pcb->mss;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F
+ " ssthresh %"U16_F"\n",
+ pcb->cwnd, pcb->ssthresh));
+
+ /* The following needs to be called AFTER cwnd is set to one
+ mss - STJ */
+ tcp_rexmit_rto(pcb);
+ }
+ }
+ }
+ /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+ if (pcb->state == FIN_WAIT_2) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+ }
+ }
+
+ /* Check if KEEPALIVE should be sent */
+ if((pcb->so_options & SOF_KEEPALIVE) &&
+ ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))) {
+#if LWIP_TCP_KEEPALIVE
+ if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl))
+ / TCP_SLOW_INTERVAL)
+#else
+ if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
+#endif /* LWIP_TCP_KEEPALIVE */
+ {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+ ++pcb_remove;
+ ++pcb_reset;
+ }
+#if LWIP_TCP_KEEPALIVE
+ else if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl)
+ / TCP_SLOW_INTERVAL)
+#else
+ else if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT)
+ / TCP_SLOW_INTERVAL)
+#endif /* LWIP_TCP_KEEPALIVE */
+ {
+ tcp_keepalive(pcb);
+ pcb->keep_cnt_sent++;
+ }
+ }
+
+ /* If this PCB has queued out of sequence data, but has been
+ inactive for too long, will drop the data (it will eventually
+ be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL &&
+ (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Check if this PCB has stayed too long in SYN-RCVD */
+ if (pcb->state == SYN_RCVD) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
+ }
+ }
+
+ /* Check if this PCB has stayed too long in LAST-ACK */
+ if (pcb->state == LAST_ACK) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
+ }
+ }
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_active_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+ tcp_active_pcbs = pcb->next;
+ }
+
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT);
+ if (pcb_reset) {
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+ }
+
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+ } else {
+ /* get the 'next' element now and work with 'prev' below (in case of abort) */
+ prev = pcb;
+ pcb = pcb->next;
+
+ /* We check if we should poll the connection. */
+ ++prev->polltmr;
+ if (prev->polltmr >= prev->pollinterval) {
+ prev->polltmr = 0;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+ TCP_EVENT_POLL(prev, err);
+ /* if err == ERR_ABRT, 'prev' is already deallocated */
+ if (err == ERR_OK) {
+ tcp_output(prev);
+ }
+ }
+ }
+ }
+
+
+ /* Steps through all of the TIME-WAIT PCBs. */
+ prev = NULL;
+ pcb = tcp_tw_pcbs;
+ while (pcb != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ pcb_remove = 0;
+
+ /* Check if this PCB has stayed long enough in TIME-WAIT */
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ }
+
+
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_tw_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+ tcp_tw_pcbs = pcb->next;
+ }
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+ } else {
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_fasttmr(void)
+{
+ struct tcp_pcb *pcb = tcp_active_pcbs;
+
+ while(pcb != NULL) {
+ struct tcp_pcb *next = pcb->next;
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ /* Notify again application with data previously received. */
+ err_t err;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n"));
+ TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
+ if (err == ERR_OK) {
+ pcb->refused_data = NULL;
+ } else if (err == ERR_ABRT) {
+ /* if err == ERR_ABRT, 'pcb' is already deallocated */
+ pcb = NULL;
+ }
+ }
+
+ /* send delayed ACKs */
+ if (pcb && (pcb->flags & TF_ACK_DELAY)) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+
+ pcb = next;
+ }
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+ while (seg != NULL) {
+ struct tcp_seg *next = seg->next;
+ tcp_seg_free(seg);
+ seg = next;
+ }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+ if (seg != NULL) {
+ if (seg->p != NULL) {
+ pbuf_free(seg->p);
+#if TCP_DEBUG
+ seg->p = NULL;
+#endif /* TCP_DEBUG */
+ }
+ memp_free(MEMP_TCP_SEG, seg);
+ }
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+ pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+ struct tcp_seg *cseg;
+
+ cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
+ if (cseg == NULL) {
+ return NULL;
+ }
+ SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+ pbuf_ref(cseg->p);
+ return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ } else if (err == ERR_OK) {
+ return tcp_close(pcb);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Kills the oldest active connection that has lower priority than prio.
+ *
+ * @param prio minimum priority
+ */
+static void
+tcp_kill_prio(u8_t prio)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+ u8_t mprio;
+
+
+ mprio = TCP_PRIO_MAX;
+
+ /* We kill the oldest active connection that has lower priority than prio. */
+ inactivity = 0;
+ inactive = NULL;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->prio <= prio &&
+ pcb->prio <= mprio &&
+ (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ mprio = pcb->prio;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void
+tcp_kill_timewait(void)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+
+ inactivity = 0;
+ inactive = NULL;
+ /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *
+ * @param prio priority for the new pcb
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+ struct tcp_pcb *pcb;
+ u32_t iss;
+
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing oldest connection in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
+ tcp_kill_timewait();
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing active connections with lower priority than the new one. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
+ tcp_kill_prio(prio);
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed twice before */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* adjust err stats: timewait PCB was freed above */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ memset(pcb, 0, sizeof(struct tcp_pcb));
+ pcb->prio = prio;
+ pcb->snd_buf = TCP_SND_BUF;
+ pcb->snd_queuelen = 0;
+ pcb->rcv_wnd = TCP_WND;
+ pcb->rcv_ann_wnd = TCP_WND;
+ pcb->tos = 0;
+ pcb->ttl = TCP_TTL;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
+ pcb->rto = 3000 / TCP_SLOW_INTERVAL;
+ pcb->sa = 0;
+ pcb->sv = 3000 / TCP_SLOW_INTERVAL;
+ pcb->rtime = -1;
+ pcb->cwnd = 1;
+ iss = tcp_next_iss();
+ pcb->snd_wl2 = iss;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss;
+ pcb->snd_lbb = iss;
+ pcb->tmr = tcp_ticks;
+
+ pcb->polltmr = 0;
+
+#if LWIP_CALLBACK_API
+ pcb->recv = tcp_recv_null;
+#endif /* LWIP_CALLBACK_API */
+
+ /* Init KEEPALIVE timer */
+ pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
+
+#if LWIP_TCP_KEEPALIVE
+ pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+ pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+ pcb->keep_cnt_sent = 0;
+ }
+ return pcb;
+}
+
+/**
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new(void)
+{
+ return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/**
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{
+ pcb->callback_arg = arg;
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+ pcb->recv = recv;
+}
+
+/**
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+ pcb->sent = sent;
+}
+
+/**
+ * Used to specify the function that should be called when a fatal error
+ * has occured on the connection.
+ *
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ * has occured on the connection
+ */
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+ pcb->errf = err;
+}
+
+/**
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ * connection has been connected to another host
+ */
+void
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
+{
+ pcb->accept = accept;
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * Used to specify the function that should be called periodically
+ * from TCP. The interval is specified in terms of the TCP coarse
+ * timer interval, which is called twice a second.
+ *
+ */
+void
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
+{
+#if LWIP_CALLBACK_API
+ pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */
+ pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+ if (pcb->state != CLOSED &&
+ pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN) {
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+
+#if TCP_LISTEN_BACKLOG
+ if (pcb->state == SYN_RCVD) {
+ /* Need to find the corresponding listen_pcb and decrease its accepts_pending */
+ struct tcp_pcb_listen *lpcb;
+ LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
+ tcp_listen_pcbs.listen_pcbs != NULL);
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if ((lpcb->local_port == pcb->local_port) &&
+ (ip_addr_isany(&lpcb->local_ip) ||
+ ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) {
+ /* port and address of the listen pcb match the timed-out pcb */
+ LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
+ lpcb->accepts_pending > 0);
+ lpcb->accepts_pending--;
+ break;
+ }
+ }
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+
+
+ if (pcb->refused_data != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ if (pcb->unsent != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+ }
+ if (pcb->unacked != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+ }
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Stop the retransmission timer as it will expect data on unacked
+ queue if it fires */
+ pcb->rtime = -1;
+
+ tcp_segs_free(pcb->unsent);
+ tcp_segs_free(pcb->unacked);
+ pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+ }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+ TCP_RMV(pcblist, pcb);
+
+ tcp_pcb_purge(pcb);
+
+ /* if there is an outstanding delayed ACKs, send it */
+ if (pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN &&
+ pcb->flags & TF_ACK_DELAY) {
+ pcb->flags |= TF_ACK_NOW;
+ tcp_output(pcb);
+ }
+
+ if (pcb->state != LISTEN) {
+ LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+ LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+ LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+ }
+
+ pcb->state = CLOSED;
+
+ LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ * @return u32_t pseudo random sequence number
+ */
+u32_t
+tcp_next_iss(void)
+{
+ static u32_t iss = 6510;
+
+ iss += tcp_ticks; /* XXX */
+ return iss;
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calcluates the effective send mss that can be used for a specific IP address
+ * by using ip_route to determin the netif used to send to the address and
+ * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ */
+u16_t
+tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr)
+{
+ u16_t mss_s;
+ struct netif *outif;
+
+ outif = ip_route(addr);
+ if ((outif != NULL) && (outif->mtu != 0)) {
+ mss_s = outif->mtu - IP_HLEN - TCP_HLEN;
+ /* RFC 1122, chap 4.2.2.6:
+ * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+ * We correct for TCP options in tcp_write(), and don't support IP options.
+ */
+ sendmss = LWIP_MIN(sendmss, mss_s);
+ }
+ return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+const char*
+tcp_debug_state_str(enum tcp_state s)
+{
+ return tcp_state_str[s];
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ ntohs(tcphdr->src), ntohs(tcphdr->dest)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
+ ntohl(tcphdr->seqno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
+ ntohl(tcphdr->ackno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (",
+ TCPH_HDRLEN(tcphdr),
+ TCPH_FLAGS(tcphdr) >> 5 & 1,
+ TCPH_FLAGS(tcphdr) >> 4 & 1,
+ TCPH_FLAGS(tcphdr) >> 3 & 1,
+ TCPH_FLAGS(tcphdr) >> 2 & 1,
+ TCPH_FLAGS(tcphdr) >> 1 & 1,
+ TCPH_FLAGS(tcphdr) & 1,
+ ntohs(tcphdr->wnd)));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n",
+ ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(u8_t flags)
+{
+ if (flags & TCP_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+ }
+ if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+ }
+ if (flags & TCP_RST) {
+ LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+ }
+ if (flags & TCP_PSH) {
+ LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+ }
+ if (flags & TCP_ACK) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+ }
+ if (flags & TCP_URG) {
+ LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+ }
+ if (flags & TCP_ECE) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+ }
+ if (flags & TCP_CWR) {
+ LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+ struct tcp_pcb *pcb;
+ LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+ for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t
+tcp_pcbs_sane(void)
+{
+ struct tcp_pcb *pcb;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ }
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ }
+ return 1;
+}
+#endif /* TCP_DEBUG */
+
+#endif /* LWIP_TCP */
diff --git a/core/lwip/src/core/tcp_in.c b/core/lwip/src/core/tcp_in.c
new file mode 100644
index 00000000..90952648
--- /dev/null
+++ b/core/lwip/src/core/tcp_in.c
@@ -0,0 +1,1567 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp_impl.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "arch/perf.h"
+
+/* These variables are global to all functions involved in the input
+ processing of TCP segments. They are set by the tcp_input()
+ function. */
+static struct tcp_seg inseg;
+static struct tcp_hdr *tcphdr;
+static struct ip_hdr *iphdr;
+static u32_t seqno, ackno;
+static u8_t flags;
+static u16_t tcplen;
+
+static u8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static err_t tcp_process(struct tcp_pcb *pcb);
+static void tcp_receive(struct tcp_pcb *pcb);
+static void tcp_parseopt(struct tcp_pcb *pcb);
+
+static err_t tcp_listen_input(struct tcp_pcb_listen *pcb);
+static err_t tcp_timewait_input(struct tcp_pcb *pcb);
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the IP header)
+ * @param inp network interface on which this segment was received
+ */
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+ struct tcp_pcb *pcb, *prev;
+ struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+ struct tcp_pcb *lpcb_prev = NULL;
+ struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+ u8_t hdrlen;
+ err_t err;
+
+ PERF_START;
+
+ TCP_STATS_INC(tcp.recv);
+ snmp_inc_tcpinsegs();
+
+ iphdr = (struct ip_hdr *)p->payload;
+ tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
+
+#if TCP_INPUT_DEBUG
+ tcp_debug_print(tcphdr);
+#endif
+
+ /* remove header from payload */
+ if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+ TCP_STATS_INC(tcp.lenerr);
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+
+ /* Don't even process incoming broadcasts/multicasts. */
+ if (ip_addr_isbroadcast(&current_iphdr_dest, inp) ||
+ ip_addr_ismulticast(&current_iphdr_dest)) {
+ TCP_STATS_INC(tcp.proterr);
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+
+#if CHECKSUM_CHECK_TCP
+ /* Verify TCP checksum. */
+ if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+ IP_PROTO_TCP, p->tot_len) != 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+ inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+ IP_PROTO_TCP, p->tot_len)));
+#if TCP_DEBUG
+ tcp_debug_print(tcphdr);
+#endif /* TCP_DEBUG */
+ TCP_STATS_INC(tcp.chkerr);
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+#endif
+
+ /* Move the payload pointer in the pbuf so that it points to the
+ TCP data instead of the TCP header. */
+ hdrlen = TCPH_HDRLEN(tcphdr);
+ if(pbuf_header(p, -(hdrlen * 4))){
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
+ TCP_STATS_INC(tcp.lenerr);
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+
+ /* Convert fields in TCP header to host byte order. */
+ tcphdr->src = ntohs(tcphdr->src);
+ tcphdr->dest = ntohs(tcphdr->dest);
+ seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
+ ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
+ tcphdr->wnd = ntohs(tcphdr->wnd);
+
+ flags = TCPH_FLAGS(tcphdr);
+ tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
+
+ /* Demultiplex an incoming segment. First, we check if it is destined
+ for an active connection. */
+ prev = NULL;
+
+
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src) &&
+ ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest)) {
+
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+ if (prev != NULL) {
+ prev->next = pcb->next;
+ pcb->next = tcp_active_pcbs;
+ tcp_active_pcbs = pcb;
+ }
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+ break;
+ }
+ prev = pcb;
+ }
+
+ if (pcb == NULL) {
+ /* If it did not go to an active connection, we check the connections
+ in the TIME-WAIT state. */
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src) &&
+ ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest)) {
+ /* We don't really care enough to move this PCB to the front
+ of the list since we are not very likely to receive that
+ many segments for connections in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+ tcp_timewait_input(pcb);
+ pbuf_free(p);
+ return;
+ }
+ }
+
+ /* Finally, if we still did not get a match, we check all PCBs that
+ are LISTENing for incoming connections. */
+ prev = NULL;
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if (lpcb->local_port == tcphdr->dest) {
+#if SO_REUSE
+ if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest)) {
+ /* found an exact match */
+ break;
+ } else if(ip_addr_isany(&(lpcb->local_ip))) {
+ /* found an ANY-match */
+ lpcb_any = lpcb;
+ lpcb_prev = prev;
+ }
+#else /* SO_REUSE */
+ if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest) ||
+ ip_addr_isany(&(lpcb->local_ip))) {
+ /* found a match */
+ break;
+ }
+#endif /* SO_REUSE */
+ }
+ prev = (struct tcp_pcb *)lpcb;
+ }
+#if SO_REUSE
+ /* first try specific local IP */
+ if (lpcb == NULL) {
+ /* only pass to ANY if no specific local IP has been found */
+ lpcb = lpcb_any;
+ prev = lpcb_prev;
+ }
+#endif /* SO_REUSE */
+ if (lpcb != NULL) {
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ if (prev != NULL) {
+ ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+ /* our successor is the remainder of the listening list */
+ lpcb->next = tcp_listen_pcbs.listen_pcbs;
+ /* put this listening pcb at the head of the listening list */
+ tcp_listen_pcbs.listen_pcbs = lpcb;
+ }
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+ tcp_listen_input(lpcb);
+ pbuf_free(p);
+ return;
+ }
+ }
+
+#if TCP_INPUT_DEBUG
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+ if (pcb != NULL) {
+ /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+
+ /* Set up a tcp_seg structure. */
+ inseg.next = NULL;
+ inseg.len = p->tot_len;
+ inseg.p = p;
+ inseg.tcphdr = tcphdr;
+
+ recv_data = NULL;
+ recv_flags = 0;
+
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ /* Notify again application with data previously received. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+ TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
+ if (err == ERR_OK) {
+ pcb->refused_data = NULL;
+ } else if ((err == ERR_ABRT) || (tcplen > 0)) {
+ /* if err == ERR_ABRT, 'pcb' is already deallocated */
+ /* Drop incoming packets because pcb is "full" (only if the incoming
+ segment contains data). */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+ }
+ tcp_input_pcb = pcb;
+ err = tcp_process(pcb);
+ /* A return value of ERR_ABRT means that tcp_abort() was called
+ and that the pcb has been freed. If so, we don't do anything. */
+ if (err != ERR_ABRT) {
+ if (recv_flags & TF_RESET) {
+ /* TF_RESET means that the connection was reset by the other
+ end. We then call the error callback to inform the
+ application that the connection is dead before we
+ deallocate the PCB. */
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else if (recv_flags & TF_CLOSED) {
+ /* The connection has been closed and we will deallocate the
+ PCB. */
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ err = ERR_OK;
+ /* If the application has registered a "sent" function to be
+ called when new send buffer space is available, we call it
+ now. */
+ if (pcb->acked > 0) {
+ TCP_EVENT_SENT(pcb, pcb->acked, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+
+ if (recv_data != NULL) {
+ LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+ if (pcb->flags & TF_RXCLOSED) {
+ /* received data although already closed -> abort (send RST) to
+ notify the remote host that not all data has been processed */
+ pbuf_free(recv_data);
+ tcp_abort(pcb);
+ goto aborted;
+ }
+ if (flags & TCP_PSH) {
+ recv_data->flags |= PBUF_FLAG_PUSH;
+ }
+
+ /* Notify application that data has been received. */
+ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+
+ /* If the upper layer can't receive this data, store it */
+ if (err != ERR_OK) {
+ pcb->refused_data = recv_data;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+ }
+ }
+
+ /* If a FIN segment was received, we call the callback
+ function with a NULL buffer to indicate EOF. */
+ if (recv_flags & TF_GOT_FIN) {
+ /* correct rcv_wnd as the application won't call tcp_recved()
+ for the FIN's seqno */
+ if (pcb->rcv_wnd != TCP_WND) {
+ pcb->rcv_wnd++;
+ }
+ TCP_EVENT_CLOSED(pcb, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+
+ tcp_input_pcb = NULL;
+ /* Try to send something out. */
+ tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+ }
+ }
+ /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+ Below this line, 'pcb' may not be dereferenced! */
+aborted:
+ tcp_input_pcb = NULL;
+ recv_data = NULL;
+
+ /* give up our reference to inseg.p */
+ if (inseg.p != NULL)
+ {
+ pbuf_free(inseg.p);
+ inseg.p = NULL;
+ }
+ } else {
+
+ /* If no matching PCB was found, send a TCP RST (reset) to the
+ sender. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+ if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+ TCP_STATS_INC(tcp.proterr);
+ TCP_STATS_INC(tcp.drop);
+ tcp_rst(ackno, seqno + tcplen,
+ ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ }
+ pbuf_free(p);
+ }
+
+ LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+ PERF_STOP("tcp_input");
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ * @return ERR_OK if the segment was processed
+ * another err_t on error
+ *
+ * @note the return value is not (yet?) used in tcp_input()
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_listen_input(struct tcp_pcb_listen *pcb)
+{
+ struct tcp_pcb *npcb;
+ err_t rc;
+
+ /* In the LISTEN state, we check for incoming SYN segments,
+ creates a new PCB, and responds with a SYN|ACK. */
+ if (flags & TCP_ACK) {
+ /* For incoming segments with the ACK flag set, respond with a
+ RST. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+ tcp_rst(ackno + 1, seqno + tcplen,
+ ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ } else if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+ if (pcb->accepts_pending >= pcb->backlog) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+ return ERR_ABRT;
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+ npcb = tcp_alloc(pcb->prio);
+ /* If a new PCB could not be created (probably due to lack of memory),
+ we don't do anything, but rely on the sender will retransmit the
+ SYN at a time when we have more memory available. */
+ if (npcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+#if TCP_LISTEN_BACKLOG
+ pcb->accepts_pending++;
+#endif /* TCP_LISTEN_BACKLOG */
+ /* Set up the new PCB. */
+ ip_addr_copy(npcb->local_ip, current_iphdr_dest);
+ npcb->local_port = pcb->local_port;
+ ip_addr_copy(npcb->remote_ip, current_iphdr_src);
+ npcb->remote_port = tcphdr->src;
+ npcb->state = SYN_RCVD;
+ npcb->rcv_nxt = seqno + 1;
+ npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+ npcb->snd_wnd = tcphdr->wnd;
+ npcb->ssthresh = npcb->snd_wnd;
+ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+ npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API
+ npcb->accept = pcb->accept;
+#endif /* LWIP_CALLBACK_API */
+ /* inherit socket options */
+ npcb->so_options = pcb->so_options & SOF_INHERITED;
+ /* Register the new PCB so that we can begin receiving segments
+ for it. */
+ TCP_REG(&tcp_active_pcbs, npcb);
+
+ /* Parse any options in the SYN. */
+ tcp_parseopt(npcb);
+#if TCP_CALCULATE_EFF_SEND_MSS
+ npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ snmp_inc_tcppassiveopens();
+
+ /* Send a SYN|ACK together with the MSS option. */
+ rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+ if (rc != ERR_OK) {
+ tcp_abandon(npcb, 0);
+ return rc;
+ }
+ return tcp_output(npcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+ /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+ /* RFC 793 3.9 Event Processing - Segment Arrives:
+ * - first check sequence number - we skip that one in TIME_WAIT (always
+ * acceptable since we only send ACKs)
+ * - second check the RST bit (... return) */
+ if (flags & TCP_RST) {
+ return ERR_OK;
+ }
+ /* - fourth, check the SYN bit, */
+ if (flags & TCP_SYN) {
+ /* If an incoming segment is not acceptable, an acknowledgment
+ should be sent in reply */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
+ /* If the SYN is in the window it is an error, send a reset */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ return ERR_OK;
+ }
+ } else if (flags & TCP_FIN) {
+ /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+ Restart the 2 MSL time-wait timeout.*/
+ pcb->tmr = tcp_ticks;
+ }
+
+ if ((tcplen > 0)) {
+ /* Acknowledge data, FIN or out-of-window SYN */
+ pcb->flags |= TF_ACK_NOW;
+ return tcp_output(pcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *rseg;
+ u8_t acceptable = 0;
+ err_t err;
+
+ err = ERR_OK;
+
+ /* Process incoming RST segments. */
+ if (flags & TCP_RST) {
+ /* First, determine if the reset is acceptable. */
+ if (pcb->state == SYN_SENT) {
+ if (ackno == pcb->snd_nxt) {
+ acceptable = 1;
+ }
+ } else {
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt+pcb->rcv_wnd)) {
+ acceptable = 1;
+ }
+ }
+
+ if (acceptable) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+ LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+ recv_flags |= TF_RESET;
+ pcb->flags &= ~TF_ACK_DELAY;
+ return ERR_RST;
+ } else {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ return ERR_OK;
+ }
+ }
+
+ if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
+ /* Cope with new connection attempt after remote end crashed */
+ tcp_ack_now(pcb);
+ return ERR_OK;
+ }
+
+ if ((pcb->flags & TF_RXCLOSED) == 0) {
+ /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+ pcb->tmr = tcp_ticks;
+ }
+ pcb->keep_cnt_sent = 0;
+
+ tcp_parseopt(pcb);
+
+ /* Do different things depending on the TCP state. */
+ switch (pcb->state) {
+ case SYN_SENT:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+ pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
+ /* received SYN ACK with expected sequence number? */
+ if ((flags & TCP_ACK) && (flags & TCP_SYN)
+ && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
+ pcb->snd_buf++;
+ pcb->rcv_nxt = seqno + 1;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->lastack = ackno;
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+ pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
+ * but for the default value of pcb->mss) */
+ pcb->ssthresh = pcb->mss * 10;
+
+ pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+ LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+ --pcb->snd_queuelen;
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+ rseg = pcb->unacked;
+ pcb->unacked = rseg->next;
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if(pcb->unacked == NULL)
+ pcb->rtime = -1;
+ else {
+ pcb->rtime = 0;
+ pcb->nrtx = 0;
+ }
+
+ tcp_seg_free(rseg);
+
+ /* Call the user specified function to call when sucessfully
+ * connected. */
+ TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ tcp_ack_now(pcb);
+ }
+ /* received ACK? possibly a half-open connection */
+ else if (flags & TCP_ACK) {
+ /* send a RST to bring the other side in a non-synchronized state. */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ }
+ break;
+ case SYN_RCVD:
+ if (flags & TCP_ACK) {
+ /* expected ACK number? */
+ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+ u16_t old_cwnd;
+ pcb->state = ESTABLISHED;
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API
+ LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+#endif
+ /* Call the accept function. */
+ TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
+ if (err != ERR_OK) {
+ /* If the accept function returns with an error, we abort
+ * the connection. */
+ /* Already aborted? */
+ if (err != ERR_ABRT) {
+ tcp_abort(pcb);
+ }
+ return ERR_ABRT;
+ }
+ old_cwnd = pcb->cwnd;
+ /* If there was any data contained within this ACK,
+ * we'd better pass it on to the application as well. */
+ tcp_receive(pcb);
+
+ /* Prevent ACK for SYN to generate a sent event */
+ if (pcb->acked != 0) {
+ pcb->acked--;
+ }
+
+ pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+
+ if (recv_flags & TF_GOT_FIN) {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ } else {
+ /* incorrect ACK number, send RST */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ }
+ } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+ /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+ tcp_rexmit(pcb);
+ }
+ break;
+ case CLOSE_WAIT:
+ /* FALLTHROUGH */
+ case ESTABLISHED:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) { /* passive close */
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ break;
+ case FIN_WAIT_1:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ } else {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSING;
+ }
+ } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+ pcb->state = FIN_WAIT_2;
+ }
+ break;
+ case FIN_WAIT_2:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case CLOSING:
+ tcp_receive(pcb);
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_pcb_purge(pcb);
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case LAST_ACK:
+ tcp_receive(pcb);
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+ recv_flags |= TF_CLOSED;
+ }
+ break;
+ default:
+ break;
+ }
+ return ERR_OK;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+ struct tcp_seg *old_seg;
+
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ /* received segment overlaps all following segments */
+ tcp_segs_free(next);
+ next = NULL;
+ }
+ else {
+ /* delete some following segments
+ oos queue may have segments with FIN flag */
+ while (next &&
+ TCP_SEQ_GEQ((seqno + cseg->len),
+ (next->tcphdr->seqno + next->len))) {
+ /* cseg with FIN already processed */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+ }
+ old_seg = next;
+ next = next->next;
+ tcp_seg_free(old_seg);
+ }
+ if (next &&
+ TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+ /* We need to trim the incoming segment. */
+ cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
+ pbuf_realloc(cseg->p, cseg->len);
+ }
+ }
+ cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, is places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * i it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+ struct pbuf *p;
+ s32_t off;
+ s16_t m;
+ u32_t right_wnd_edge;
+ u16_t new_tot_len;
+ int found_dupack = 0;
+
+ if (flags & TCP_ACK) {
+ right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+ /* Update window. */
+ if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+ (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+ (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wl1 = seqno;
+ pcb->snd_wl2 = ackno;
+ if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {
+ pcb->persist_backoff = 0;
+ }
+ LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+ } else {
+ if (pcb->snd_wnd != tcphdr->wnd) {
+ LWIP_DEBUGF(TCP_WND_DEBUG,
+ ("tcp_receive: no window update lastack %"U32_F" ackno %"
+ U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+ pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+ }
+#endif /* TCP_WND_DEBUG */
+ }
+
+ /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+ * duplicate ack if:
+ * 1) It doesn't ACK new data
+ * 2) length of received packet is zero (i.e. no payload)
+ * 3) the advertised window hasn't changed
+ * 4) There is outstanding unacknowledged data (retransmission timer running)
+ * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+ *
+ * If it passes all five, should process as a dupack:
+ * a) dupacks < 3: do nothing
+ * b) dupacks == 3: fast retransmit
+ * c) dupacks > 3: increase cwnd
+ *
+ * If it only passes 1-3, should reset dupack counter (and add to
+ * stats, which we don't do in lwIP)
+ *
+ * If it only passes 1, should reset dupack counter
+ *
+ */
+
+ /* Clause 1 */
+ if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+ pcb->acked = 0;
+ /* Clause 2 */
+ if (tcplen == 0) {
+ /* Clause 3 */
+ if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
+ /* Clause 4 */
+ if (pcb->rtime >= 0) {
+ /* Clause 5 */
+ if (pcb->lastack == ackno) {
+ found_dupack = 1;
+ if (pcb->dupacks + 1 > pcb->dupacks)
+ ++pcb->dupacks;
+ if (pcb->dupacks > 3) {
+ /* Inflate the congestion window, but not if it means that
+ the value overflows. */
+ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ } else if (pcb->dupacks == 3) {
+ /* Do fast retransmit */
+ tcp_rexmit_fast(pcb);
+ }
+ }
+ }
+ }
+ }
+ /* If Clause (1) or more is true, but not a duplicate ack, reset
+ * count of consecutive duplicate acks */
+ if (!found_dupack) {
+ pcb->dupacks = 0;
+ }
+ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
+ /* We come here when the ACK acknowledges new data. */
+
+ /* Reset the "IN Fast Retransmit" flag, since we are no longer
+ in fast retransmit. Also reset the congestion window to the
+ slow start threshold. */
+ if (pcb->flags & TF_INFR) {
+ pcb->flags &= ~TF_INFR;
+ pcb->cwnd = pcb->ssthresh;
+ }
+
+ /* Reset the number of retransmissions. */
+ pcb->nrtx = 0;
+
+ /* Reset the retransmission time-out. */
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ /* Update the send buffer space. Diff between the two can never exceed 64K? */
+ pcb->acked = (u16_t)(ackno - pcb->lastack);
+
+ pcb->snd_buf += pcb->acked;
+
+ /* Reset the fast retransmit variables. */
+ pcb->dupacks = 0;
+ pcb->lastack = ackno;
+
+ /* Update the congestion control variables (cwnd and
+ ssthresh). */
+ if (pcb->state >= ESTABLISHED) {
+ if (pcb->cwnd < pcb->ssthresh) {
+ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+ } else {
+ u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+ if (new_cwnd > pcb->cwnd) {
+ pcb->cwnd = new_cwnd;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
+ }
+ }
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+ ackno,
+ pcb->unacked != NULL?
+ ntohl(pcb->unacked->tcphdr->seqno): 0,
+ pcb->unacked != NULL?
+ ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+ /* Remove segment from the unacknowledged list if the incoming
+ ACK acknowlegdes them. */
+ while (pcb->unacked != NULL &&
+ TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked), ackno)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+ ntohl(pcb->unacked->tcphdr->seqno),
+ ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked)));
+
+ next = pcb->unacked;
+ pcb->unacked = pcb->unacked->next;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+ /* Prevent ACK for FIN to generate a sent event */
+ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+ pcb->acked--;
+ }
+
+ pcb->snd_queuelen -= pbuf_clen(next->p);
+ tcp_seg_free(next);
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ }
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if(pcb->unacked == NULL)
+ pcb->rtime = -1;
+ else
+ pcb->rtime = 0;
+
+ pcb->polltmr = 0;
+ } else {
+ /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
+ pcb->acked = 0;
+ }
+
+ /* We go through the ->unsent list to see if any of the segments
+ on the list are acknowledged by the ACK. This may seem
+ strange since an "unsent" segment shouldn't be acked. The
+ rationale is that lwIP puts all outstanding segments on the
+ ->unsent list after a retransmission, so these segments may
+ in fact have been sent once. */
+ while (pcb->unsent != NULL &&
+ TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+ ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent)));
+
+ next = pcb->unsent;
+ pcb->unsent = pcb->unsent->next;
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+ /* Prevent ACK for FIN to generate a sent event */
+ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+ pcb->acked--;
+ }
+ pcb->snd_queuelen -= pbuf_clen(next->p);
+ tcp_seg_free(next);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+ }
+ /* End of ACK for new data processing. */
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+ pcb->rttest, pcb->rtseq, ackno));
+
+ /* RTT estimation calculations. This is done by checking if the
+ incoming segment acknowledges the segment we use to take a
+ round-trip time measurement. */
+ if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
+ /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+ and a round-trip shouldn't be that long... */
+ m = (s16_t)(tcp_ticks - pcb->rttest);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+ m, m * TCP_SLOW_INTERVAL));
+
+ /* This is taken directly from VJs original code in his paper */
+ m = m - (pcb->sa >> 3);
+ pcb->sa += m;
+ if (m < 0) {
+ m = -m;
+ }
+ m = m - (pcb->sv >> 2);
+ pcb->sv += m;
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+ pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+
+ pcb->rttest = 0;
+ }
+ }
+
+ /* If the incoming segment contains data, we must process it
+ further. */
+ if (tcplen > 0) {
+ /* This code basically does three things:
+
+ +) If the incoming segment contains data that is the next
+ in-sequence data, this data is passed to the application. This
+ might involve trimming the first edge of the data. The rcv_nxt
+ variable and the advertised window are adjusted.
+
+ +) If the incoming segment has data that is above the next
+ sequence number expected (->rcv_nxt), the segment is placed on
+ the ->ooseq queue. This is done by finding the appropriate
+ place in the ->ooseq queue (which is ordered by sequence
+ number) and trim the segment in both ends if needed. An
+ immediate ACK is sent to indicate that we received an
+ out-of-sequence segment.
+
+ +) Finally, we check if the first segment on the ->ooseq queue
+ now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+ rcv_nxt > ooseq->seqno, we must trim the first edge of the
+ segment on ->ooseq before we adjust rcv_nxt. The data in the
+ segments that are now on sequence are chained onto the
+ incoming segment so that we only need to call the application
+ once.
+ */
+
+ /* First, we check if we must trim the first edge. We have to do
+ this if the sequence number of the incoming segment is less
+ than rcv_nxt, and the sequence number plus the length of the
+ segment is larger than rcv_nxt. */
+ /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+ if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+ if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
+ /* Trimming the first edge is done by pushing the payload
+ pointer in the pbuf downwards. This is somewhat tricky since
+ we do not want to discard the full contents of the pbuf up to
+ the new starting point of the data since we have to keep the
+ TCP header which is present in the first pbuf in the chain.
+
+ What is done is really quite a nasty hack: the first pbuf in
+ the pbuf chain is pointed to by inseg.p. Since we need to be
+ able to deallocate the whole pbuf, we cannot change this
+ inseg.p pointer to point to any of the later pbufs in the
+ chain. Instead, we point the ->payload pointer in the first
+ pbuf to data in one of the later pbufs. We also set the
+ inseg.data pointer to point to the right place. This way, the
+ ->p pointer will still point to the first pbuf, but the
+ ->p->payload pointer will point to data in another pbuf.
+
+ After we are done with adjusting the pbuf pointers we must
+ adjust the ->data pointer in the seg and the segment
+ length.*/
+
+ off = pcb->rcv_nxt - seqno;
+ p = inseg.p;
+ LWIP_ASSERT("inseg.p != NULL", inseg.p);
+ LWIP_ASSERT("insane offset!", (off < 0x7fff));
+ if (inseg.p->len < off) {
+ LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+ new_tot_len = (u16_t)(inseg.p->tot_len - off);
+ while (p->len < off) {
+ off -= p->len;
+ /* KJM following line changed (with addition of new_tot_len var)
+ to fix bug #9076
+ inseg.p->tot_len -= p->len; */
+ p->tot_len = new_tot_len;
+ p->len = 0;
+ p = p->next;
+ }
+ if(pbuf_header(p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ } else {
+ if(pbuf_header(inseg.p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ }
+ inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
+ inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
+ }
+ else {
+ if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+ /* the whole segment is < rcv_nxt */
+ /* must be a duplicate of a packet that has already been correctly handled */
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+ tcp_ack_now(pcb);
+ }
+ }
+
+ /* The sequence number must be within the window (above rcv_nxt
+ and below rcv_nxt + rcv_wnd) in order to be further
+ processed. */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt + pcb->rcv_wnd - 1)){
+ if (pcb->rcv_nxt == seqno) {
+ /* The incoming segment is the next in sequence. We check if
+ we have to trim the end of the segment and update rcv_nxt
+ and pass the data to the application. */
+ tcplen = TCP_TCPLEN(&inseg);
+
+ if (tcplen > pcb->rcv_wnd) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ inseg.len = pcb->rcv_wnd;
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+#if TCP_QUEUE_OOSEQ
+ /* Received in-sequence data, adjust ooseq data if:
+ - FIN has been received or
+ - inseq overlaps with ooseq */
+ if (pcb->ooseq != NULL) {
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+ /* Received in-order FIN means anything that was received
+ * out of order must now have been received in-order, so
+ * bin the ooseq queue */
+ while (pcb->ooseq != NULL) {
+ struct tcp_seg *old_ooseq = pcb->ooseq;
+ pcb->ooseq = pcb->ooseq->next;
+ tcp_seg_free(old_ooseq);
+ }
+ }
+ else {
+ next = pcb->ooseq;
+ /* Remove all segments on ooseq that are covered by inseg already.
+ * FIN is copied from ooseq to inseg if present. */
+ while (next &&
+ TCP_SEQ_GEQ(seqno + tcplen,
+ next->tcphdr->seqno + next->len)) {
+ /* inseg cannot have FIN here (already processed above) */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
+ (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+ TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+ tcplen = TCP_TCPLEN(&inseg);
+ }
+ prev = next;
+ next = next->next;
+ tcp_seg_free(prev);
+ }
+ /* Now trim right side of inseg if it overlaps with the first
+ * segment on ooseq */
+ if (next &&
+ TCP_SEQ_GT(seqno + tcplen,
+ next->tcphdr->seqno)) {
+ /* inseg cannot have FIN here (already processed above) */
+ inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+ (seqno + tcplen) == next->tcphdr->seqno);
+ }
+ pcb->ooseq = next;
+ }
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ pcb->rcv_nxt = seqno + tcplen;
+
+ /* Update the receiver's (our) window. */
+ LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+ pcb->rcv_wnd -= tcplen;
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ /* If there is data in the segment, we make preparations to
+ pass this up to the application. The ->recv_data variable
+ is used for holding the pbuf that goes to the
+ application. The code for reassembling out-of-sequence data
+ chains its data on this pbuf as well.
+
+ If the segment was a FIN, we set the TF_GOT_FIN flag that will
+ be used to indicate to the application that the remote side has
+ closed its end of the connection. */
+ if (inseg.p->tot_len > 0) {
+ recv_data = inseg.p;
+ /* Since this pbuf now is the responsibility of the
+ application, we delete our reference to it so that we won't
+ (mistakingly) deallocate it. */
+ inseg.p = NULL;
+ }
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ }
+
+#if TCP_QUEUE_OOSEQ
+ /* We now check if we have segments on the ->ooseq queue that
+ are now in sequence. */
+ while (pcb->ooseq != NULL &&
+ pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+ cseg = pcb->ooseq;
+ seqno = pcb->ooseq->tcphdr->seqno;
+
+ pcb->rcv_nxt += TCP_TCPLEN(cseg);
+ LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+ pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+ pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ if (cseg->p->tot_len > 0) {
+ /* Chain this pbuf onto the pbuf that we will pass to
+ the application. */
+ if (recv_data) {
+ pbuf_cat(recv_data, cseg->p);
+ } else {
+ recv_data = cseg->p;
+ }
+ cseg->p = NULL;
+ }
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+ pcb->state = CLOSE_WAIT;
+ }
+ }
+
+ pcb->ooseq = cseg->next;
+ tcp_seg_free(cseg);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+ /* Acknowledge the segment(s). */
+ tcp_ack(pcb);
+
+ } else {
+ /* We get here if the incoming segment is out-of-sequence. */
+ tcp_send_empty_ack(pcb);
+#if TCP_QUEUE_OOSEQ
+ /* We queue the segment on the ->ooseq queue. */
+ if (pcb->ooseq == NULL) {
+ pcb->ooseq = tcp_seg_copy(&inseg);
+ } else {
+ /* If the queue is not empty, we walk through the queue and
+ try to find a place where the sequence number of the
+ incoming segment is between the sequence numbers of the
+ previous and the next segment on the ->ooseq queue. That is
+ the place where we put the incoming segment. If needed, we
+ trim the second edges of the previous and the incoming
+ segment so that it will fit into the sequence.
+
+ If the incoming segment has the same sequence number as a
+ segment on the ->ooseq queue, we discard the segment that
+ contains less data. */
+
+ prev = NULL;
+ for(next = pcb->ooseq; next != NULL; next = next->next) {
+ if (seqno == next->tcphdr->seqno) {
+ /* The sequence number of the incoming segment is the
+ same as the sequence number of the segment on
+ ->ooseq. We check the lengths to see which one to
+ discard. */
+ if (inseg.len > next->len) {
+ /* The incoming segment is larger than the old
+ segment. We replace some segments with the new
+ one. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (prev != NULL) {
+ prev->next = cseg;
+ } else {
+ pcb->ooseq = cseg;
+ }
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ } else {
+ /* Either the lenghts are the same or the incoming
+ segment was smaller than the old one; in either
+ case, we ditch the incoming segment. */
+ break;
+ }
+ } else {
+ if (prev == NULL) {
+ if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+ /* The sequence number of the incoming segment is lower
+ than the sequence number of the first segment on the
+ queue. We put the incoming segment first on the
+ queue. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ pcb->ooseq = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ } else {
+ /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+ TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+ if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+ /* The sequence number of the incoming segment is in
+ between the sequence numbers of the previous and
+ the next segment on ->ooseq. We trim trim the previous
+ segment, delete next segments that included in received segment
+ and trim received, if needed. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+ /* We need to trim the prev segment. */
+ prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
+ pbuf_realloc(prev->p, prev->len);
+ }
+ prev->next = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ }
+ /* If the "next" segment is the last segment on the
+ ooseq queue, we add the incoming segment to the end
+ of the list. */
+ if (next->next == NULL &&
+ TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ /* segment "next" already contains all data */
+ break;
+ }
+ next->next = tcp_seg_copy(&inseg);
+ if (next->next != NULL) {
+ if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+ /* We need to trim the last segment. */
+ next->len = (u16_t)(seqno - next->tcphdr->seqno);
+ pbuf_realloc(next->p, next->len);
+ }
+ /* check if the remote side overruns our receive window */
+ if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
+ pbuf_realloc(next->next->p, next->next->len);
+ tcplen = TCP_TCPLEN(next->next);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+ }
+ break;
+ }
+ }
+ prev = next;
+ }
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ }
+ } else {
+ /* The incoming segment is not withing the window. */
+ tcp_send_empty_ack(pcb);
+ }
+ } else {
+ /* Segments with length 0 is taken care of here. Segments that
+ fall out of the window are ACKed. */
+ /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+ TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+ if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+ tcp_ack_now(pcb);
+ }
+ }
+}
+
+/**
+ * Parses the options contained in the incoming segment.
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+ u16_t c, max_c;
+ u16_t mss;
+ u8_t *opts, opt;
+#if LWIP_TCP_TIMESTAMPS
+ u32_t tsval;
+#endif
+
+ opts = (u8_t *)tcphdr + TCP_HLEN;
+
+ /* Parse the TCP MSS option, if present. */
+ if(TCPH_HDRLEN(tcphdr) > 0x5) {
+ max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
+ for (c = 0; c < max_c; ) {
+ opt = opts[c];
+ switch (opt) {
+ case 0x00:
+ /* End of options. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+ return;
+ case 0x01:
+ /* NOP option. */
+ ++c;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+ break;
+ case 0x02:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+ if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* An MSS option with the right option length. */
+ mss = (opts[c + 2] << 8) | opts[c + 3];
+ /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+ /* Advance to next option */
+ c += 0x04;
+ break;
+#if LWIP_TCP_TIMESTAMPS
+ case 0x08:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+ if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* TCP timestamp option with valid length */
+ tsval = (opts[c+2]) | (opts[c+3] << 8) |
+ (opts[c+4] << 16) | (opts[c+5] << 24);
+ if (flags & TCP_SYN) {
+ pcb->ts_recent = ntohl(tsval);
+ pcb->flags |= TF_TIMESTAMP;
+ } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+ pcb->ts_recent = ntohl(tsval);
+ }
+ /* Advance to next option */
+ c += 0x0A;
+ break;
+#endif
+ default:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+ if (opts[c + 1] == 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ /* If the length field is zero, the options are malformed
+ and we don't process them further. */
+ return;
+ }
+ /* All other options have a length field, so that we easily
+ can skip past them. */
+ c += opts[c + 1];
+ }
+ }
+ }
+}
+
+#endif /* LWIP_TCP */
diff --git a/core/lwip/src/core/tcp_out.c b/core/lwip/src/core/tcp_out.c
new file mode 100644
index 00000000..86e09195
--- /dev/null
+++ b/core/lwip/src/core/tcp_out.c
@@ -0,0 +1,1468 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp_impl.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+ nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+ len, &seg->chksum, &seg->chksum_swapped); \
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
+#endif
+
+/* Forward declarations.*/
+static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
+ u32_t seqno_be /* already in network byte order */)
+{
+ struct tcp_hdr *tcphdr;
+ struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= TCP_HLEN + optlen));
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = htons(pcb->local_port);
+ tcphdr->dest = htons(pcb->remote_port);
+ tcphdr->seqno = seqno_be;
+ tcphdr->ackno = htonl(pcb->rcv_nxt);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+ tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+ /* If we're sending a packet, update the announced right window edge */
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+ }
+ return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ERR_OK if sent, another err_t otherwise
+ */
+err_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+ /* first, try to add the fin to the last unsent segment */
+ if (pcb->unsent != NULL) {
+ struct tcp_seg *last_unsent;
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+ /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+ return ERR_OK;
+ }
+ }
+ /* no data, no length, flags, copy=1, no optdata */
+ return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
+{
+ struct tcp_seg *seg;
+ u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
+ pbuf_free(p);
+ return NULL;
+ }
+ seg->flags = optflags;
+ seg->next = NULL;
+ seg->p = p;
+ seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = 0;
+ seg->chksum_swapped = 0;
+ /* check optflags */
+ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+ (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* build TCP header */
+ if (pbuf_header(p, TCP_HLEN)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+ TCP_STATS_INC(tcp.err);
+ tcp_seg_free(seg);
+ return NULL;
+ }
+ seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+ seg->tcphdr->src = htons(pcb->local_port);
+ seg->tcphdr->dest = htons(pcb->remote_port);
+ seg->tcphdr->seqno = htonl(seqno);
+ /* ackno is set in tcp_output */
+ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+ /* wnd and chksum are set in tcp_output */
+ seg->tcphdr->urgp = 0;
+ return seg;
+}
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that willo enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ * @param
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
+ u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
+ u8_t first_seg)
+{
+ struct pbuf *p;
+ u16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ LWIP_UNUSED_ARG(max_length);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(apiflags);
+ LWIP_UNUSED_ARG(first_seg);
+ /* always create MSS-sized pbufs */
+ alloc = TCP_MSS;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (length < max_length) {
+ /* Should we allocate an oversized pbuf, or just the minimum
+ * length required? If tcp_write is going to be called again
+ * before this segment is transmitted, we want the oversized
+ * buffer. If the segment will be transmitted immediately, we can
+ * save memory by allocating only length. We use a simple
+ * heuristic based on the following information:
+ *
+ * Did the user set TCP_WRITE_FLAG_MORE?
+ *
+ * Will the Nagle algorithm defer transmission of this segment?
+ */
+ if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+ (!(pcb->flags & TF_NODELAY) &&
+ (!first_seg ||
+ pcb->unsent != NULL ||
+ pcb->unacked != NULL))) {
+ alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
+ }
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(layer, alloc, PBUF_RAM);
+ if (p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("need unchained pbuf", p->next == NULL);
+ *oversize = p->len - length;
+ /* trim p->len to the currently used size */
+ p->len = p->tot_len = length;
+ return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void
+tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
+ u8_t *seg_chksum_swapped)
+{
+ u32_t helper;
+ /* add chksum to old chksum and fold to u16_t */
+ helper = chksum + *seg_chksum;
+ chksum = FOLD_U32T(helper);
+ if ((len & 1) != 0) {
+ *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+ chksum = SWAP_BYTES_IN_WORD(chksum);
+ }
+ *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
+ */
+static err_t
+tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
+{
+ /* connection is in invalid state for data transmission? */
+ if ((pcb->state != ESTABLISHED) &&
+ (pcb->state != CLOSE_WAIT) &&
+ (pcb->state != SYN_SENT) &&
+ (pcb->state != SYN_RCVD)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+ return ERR_CONN;
+ } else if (len == 0) {
+ return ERR_OK;
+ }
+
+ /* fail on too much data */
+ if (len > pcb->snd_buf) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
+ len, pcb->snd_buf));
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ /* If total number of pbufs on the unsent/unacked queues exceeds the
+ * configured maximum, return an error */
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ } else {
+ LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+ pcb->unacked == NULL && pcb->unsent == NULL);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
+ * @return ERR_OK if enqueued, another err_t on error
+ */
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
+{
+ struct pbuf *concat_p = NULL;
+ struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+ u16_t pos = 0; /* position in 'arg' data */
+ u16_t queuelen;
+ u8_t optlen = 0;
+ u8_t optflags = 0;
+#if TCP_OVERSIZE
+ u16_t oversize = 0;
+ u16_t oversize_used = 0;
+#endif /* TCP_OVERSIZE */
+#if TCP_CHECKSUM_ON_COPY
+ u16_t concat_chksum = 0;
+ u8_t concat_chksum_swapped = 0;
+ u16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ err_t err;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Always copy to try to create single pbufs for TX */
+ apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+ (void *)pcb, arg, len, (u16_t)apiflags));
+ LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
+ arg != NULL, return ERR_ARG;);
+
+ err = tcp_write_checks(pcb, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ optflags = TF_SEG_OPTS_TS;
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+ /*
+ * TCP segmentation is done in three phases with increasing complexity:
+ *
+ * 1. Copy data directly into an oversized pbuf.
+ * 2. Chain a new pbuf to the end of pcb->unsent.
+ * 3. Create new segments.
+ *
+ * We may run out of memory at any point. In that case we must
+ * return ERR_MEM and not change anything in pcb. Therefore, all
+ * changes are recorded in local variables and committed at the end
+ * of the function. Some pcb fields are maintained in local copies:
+ *
+ * queuelen = pcb->snd_queuelen
+ * oversize = pcb->unsent_oversize
+ *
+ * These variables are set consistently by the phases:
+ *
+ * seg points to the last segment tampered with.
+ *
+ * pos records progress as data is segmented.
+ */
+
+ /* Find the tail of the unsent queue. */
+ if (pcb->unsent != NULL) {
+ u16_t space;
+ u16_t unsent_optlen;
+
+ /* @todo: this could be sped up by keeping last_unsent in the pcb */
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ /* Usable space at the end of the last unsent segment */
+ unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+ space = pcb->mss - (last_unsent->len + unsent_optlen);
+
+ /*
+ * Phase 1: Copy data directly into an oversized pbuf.
+ *
+ * The number of bytes copied is recorded in the oversize_used
+ * variable. The actual copying is done at the bottom of the
+ * function.
+ */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+ /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
+ LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+ pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ oversize = pcb->unsent_oversize;
+ if (oversize > 0) {
+ LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
+ seg = last_unsent;
+ oversize_used = oversize < len ? oversize : len;
+ pos += oversize_used;
+ oversize -= oversize_used;
+ space -= oversize_used;
+ }
+ /* now we are either finished or oversize is zero */
+ LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+ *
+ * We don't extend segments containing SYN/FIN flags or options
+ * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+ * the end.
+ */
+ if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+ u16_t seglen = space < len - pos ? space : len - pos;
+ seg = last_unsent;
+
+ /* Create a pbuf with a copy or reference to seglen bytes. We
+ * can use PBUF_RAW here since the data appears in the middle of
+ * a segment. A header will never be prepended. */
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* Data is copied */
+ if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+ seglen));
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ last_unsent->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ } else {
+ /* Data is not copied */
+ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,
+ &concat_chksum, &concat_chksum_swapped);
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ concat_p->payload = (u8_t*)arg + pos;
+ }
+
+ pos += seglen;
+ queuelen += pbuf_clen(concat_p);
+ }
+ } else {
+#if TCP_OVERSIZE
+ LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+ pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+ }
+
+ /*
+ * Phase 3: Create new segments.
+ *
+ * The new segments are chained together in the local 'queue'
+ * variable, ready to be appended to pcb->unsent.
+ */
+ while (pos < len) {
+ struct pbuf *p;
+ u16_t left = len - pos;
+ u16_t max_len = pcb->mss - optlen;
+ u16_t seglen = left > max_len ? max_len : left;
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ u8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* If copy is set, memory should be allocated and data copied
+ * into pbuf */
+ if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+ goto memerr;
+ }
+ LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+ (p->len >= seglen));
+ TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+ } else {
+ /* Copy is not set: First allocate a pbuf for holding the data.
+ * Since the referenced data is available at least until it is
+ * sent out on the link (as it has to be ACKed by the remote
+ * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+ */
+ struct pbuf *p2;
+#if TCP_OVERSIZE
+ LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+ if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ chksum = ~inet_chksum((u8_t*)arg + pos, seglen);
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ p2->payload = (u8_t*)arg + pos;
+
+ /* Second, allocate a pbuf for the headers. */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ /* If allocation fails, we have to deallocate the data pbuf as
+ * well. */
+ pbuf_free(p2);
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
+ goto memerr;
+ }
+ /* Concatenate the headers and data pbufs together. */
+ pbuf_cat(p/*header*/, p2/*data*/);
+ }
+
+ queuelen += pbuf_clen(p);
+
+ /* Now that there are more segments queued, we check again if the
+ * length of the queue exceeds the configured maximum or
+ * overflows. */
+ if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+ pbuf_free(p);
+ goto memerr;
+ }
+
+ if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = chksum;
+ seg->chksum_swapped = chksum_swapped;
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* first segment of to-be-queued data? */
+ if (queue == NULL) {
+ queue = seg;
+ } else {
+ /* Attach the segment to the end of the queued segments */
+ LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+ prev_seg->next = seg;
+ }
+ /* remember last segment of to-be-queued data for next iteration */
+ prev_seg = seg;
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+ ntohl(seg->tcphdr->seqno),
+ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+ pos += seglen;
+ }
+
+ /*
+ * All three segmentation phases were successful. We can commit the
+ * transaction.
+ */
+
+ /*
+ * Phase 1: If data has been added to the preallocated tail of
+ * last_unsent, we update the length fields of the pbuf chain.
+ */
+#if TCP_OVERSIZE
+ if (oversize_used > 0) {
+ struct pbuf *p;
+ /* Bump tot_len of whole chain, len of tail */
+ for (p = last_unsent->p; p; p = p->next) {
+ p->tot_len += oversize_used;
+ if (p->next == NULL) {
+ TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+ p->len += oversize_used;
+ }
+ }
+ last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+ last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ }
+ pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: concat_p can be concatenated onto last_unsent->p
+ */
+ if (concat_p != NULL) {
+ LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+ (last_unsent != NULL));
+ pbuf_cat(last_unsent->p, concat_p);
+ last_unsent->len += concat_p->tot_len;
+#if TCP_CHECKSUM_ON_COPY
+ if (concat_chksummed) {
+ tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+ &last_unsent->chksum_swapped);
+ last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+ }
+
+ /*
+ * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+ * is harmless
+ */
+ if (last_unsent == NULL) {
+ pcb->unsent = queue;
+ } else {
+ last_unsent->next = queue;
+ }
+
+ /*
+ * Finally update the pcb state.
+ */
+ pcb->snd_lbb += len;
+ pcb->snd_buf -= len;
+ pcb->snd_queuelen = queuelen;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+ pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ /* Set the PSH flag in the last segment that we enqueued. */
+ if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+ }
+
+ return ERR_OK;
+memerr:
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+
+ if (concat_p != NULL) {
+ pbuf_free(concat_p);
+ }
+ if (queue != NULL) {
+ tcp_segs_free(queue);
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+ return ERR_MEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ * @param optdata pointer to TCP options, or NULL.
+ * @param optlen length of TCP options in bytes.
+ */
+err_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
+{
+ struct pbuf *p;
+ struct tcp_seg *seg;
+ u8_t optflags = 0;
+ u8_t optlen = 0;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+ (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ if (flags & TCP_SYN) {
+ optflags = TF_SEG_OPTS_MSS;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ optflags |= TF_SEG_OPTS_TS;
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+ optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
+ * We need one available snd_buf byte to do that.
+ * This means we can't send FIN while snd_buf==0. A better fix would be to
+ * not include SYN and FIN sequence numbers in the snd_buf count. */
+ if (pcb->snd_buf == 0) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+
+ /* Allocate pbuf with room for TCP header + options */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+ (p->len >= optlen));
+
+ /* Allocate memory for tcp_seg, and fill in fields. */
+ if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+ ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+ ntohl(seg->tcphdr->seqno),
+ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+ (u16_t)flags));
+
+ /* Now append seg to pcb->unsent queue */
+ if (pcb->unsent == NULL) {
+ pcb->unsent = seg;
+ } else {
+ struct tcp_seg *useg;
+ for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+ useg->next = seg;
+ }
+#if TCP_OVERSIZE
+ /* The new unsent tail has no space */
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+ /* SYN and FIN bump the sequence number */
+ if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+ pcb->snd_lbb++;
+ /* optlen does not influence snd_buf */
+ pcb->snd_buf--;
+ }
+ if (flags & TCP_FIN) {
+ pcb->flags |= TF_FIN;
+ }
+
+ /* update number of segments on the queues */
+ pcb->snd_queuelen += pbuf_clen(seg->p);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ return ERR_OK;
+}
+
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
+{
+ /* Pad with two NOP options to make everything nicely aligned */
+ opts[0] = PP_HTONL(0x0101080A);
+ opts[1] = htonl(sys_now());
+ opts[2] = htonl(pcb->ts_recent);
+}
+#endif
+
+/** Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+err_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ u8_t optlen = 0;
+
+#if LWIP_TCP_TIMESTAMPS
+ if (pcb->flags & TF_TIMESTAMP) {
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif
+
+ p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+ return ERR_BUF;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+ ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+ /* remove ACK flags from the PCB, as we send an empty ACK now */
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+
+ /* NB. MSS option is only sent on SYNs, so ignore it here */
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (pcb->flags & TF_TIMESTAMP) {
+ tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
+ }
+#endif
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
+ IP_PROTO_TCP, p->tot_len);
+#endif
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ pbuf_free(p);
+
+ return ERR_OK;
+}
+
+/**
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ERR_OK if data has been sent or nothing to send
+ * another err_t on error
+ */
+err_t
+tcp_output(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg, *useg;
+ u32_t wnd, snd_nxt;
+#if TCP_CWND_DEBUG
+ s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+ /* First, check if we are invoked by the TCP input processing
+ code. If so, we do not output anything. Instead, we rely on the
+ input processing code to call us when input processing is done
+ with. */
+ if (tcp_input_pcb == pcb) {
+ return ERR_OK;
+ }
+
+ wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
+
+ seg = pcb->unsent;
+
+ /* If the TF_ACK_NOW flag is set and no data will be sent (either
+ * because the ->unsent queue is empty or because the window does
+ * not allow it), construct an empty ACK segment and send it.
+ *
+ * If data is to be sent, we will just piggyback the ACK (see below).
+ */
+ if (pcb->flags & TF_ACK_NOW &&
+ (seg == NULL ||
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+ return tcp_send_empty_ack(pcb);
+ }
+
+ /* useg should point to last segment on unacked queue */
+ useg = pcb->unacked;
+ if (useg != NULL) {
+ for (; useg->next != NULL; useg = useg->next);
+ }
+
+#if TCP_OUTPUT_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+ (void*)pcb->unsent));
+ }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
+ ", cwnd %"U16_F", wnd %"U32_F
+ ", seg == NULL, ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+ } else {
+ LWIP_DEBUGF(TCP_CWND_DEBUG,
+ ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
+ ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+ ntohl(seg->tcphdr->seqno), pcb->lastack));
+ }
+#endif /* TCP_CWND_DEBUG */
+ /* data available and window allows it to be sent? */
+ while (seg != NULL &&
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+ LWIP_ASSERT("RST not expected here!",
+ (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+ /* Stop sending if the nagle algorithm would prevent it
+ * Don't stop:
+ * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+ * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+ * either seg->next != NULL or pcb->unacked == NULL;
+ * RST is no sent using tcp_write/tcp_output.
+ */
+ if((tcp_do_output_nagle(pcb) == 0) &&
+ ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
+ break;
+ }
+#if TCP_CWND_DEBUG
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ ntohl(seg->tcphdr->seqno) + seg->len -
+ pcb->lastack,
+ ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+ ++i;
+#endif /* TCP_CWND_DEBUG */
+
+ pcb->unsent = seg->next;
+
+ if (pcb->state != SYN_SENT) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+
+ tcp_output_segment(seg, pcb);
+ snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+ if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+ pcb->snd_nxt = snd_nxt;
+ }
+ /* put segment on unacknowledged list if length > 0 */
+ if (TCP_TCPLEN(seg) > 0) {
+ seg->next = NULL;
+ /* unacked list is empty? */
+ if (pcb->unacked == NULL) {
+ pcb->unacked = seg;
+ useg = seg;
+ /* unacked list is not empty? */
+ } else {
+ /* In the case of fast retransmit, the packet should not go to the tail
+ * of the unacked queue, but rather somewhere before it. We need to check for
+ * this case. -STJ Jul 27, 2004 */
+ if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
+ /* add segment to before tail of unacked list, keeping the list sorted */
+ struct tcp_seg **cur_seg = &(pcb->unacked);
+ while (*cur_seg &&
+ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = (*cur_seg);
+ (*cur_seg) = seg;
+ } else {
+ /* add segment to tail of unacked list */
+ useg->next = seg;
+ useg = useg->next;
+ }
+ }
+ /* do not queue empty segments on the unacked list */
+ } else {
+ tcp_seg_free(seg);
+ }
+ seg = pcb->unsent;
+ }
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ /* last unsent has been removed, reset unsent_oversize */
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
+ if (seg != NULL && pcb->persist_backoff == 0 &&
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) {
+ /* prepare for persist timer */
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ }
+
+ pcb->flags &= ~TF_NAGLEMEMERR;
+ return ERR_OK;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ */
+static void
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
+{
+ u16_t len;
+ struct netif *netif;
+ u32_t *opts;
+
+ /** @bug Exclude retransmitted segments from this count. */
+ snmp_inc_tcpoutsegs();
+
+ /* The TCP header has already been constructed, but the ackno and
+ wnd fields remain. */
+ seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
+
+ /* advertise our receive window size in this TCP segment */
+ seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+ /* Add any requested options. NB MSS option is only set on SYN
+ packets, so ignore it here */
+ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
+ opts = (u32_t *)(void *)(seg->tcphdr + 1);
+ if (seg->flags & TF_SEG_OPTS_MSS) {
+ TCP_BUILD_MSS_OPTION(*opts);
+ opts += 1;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (seg->flags & TF_SEG_OPTS_TS) {
+ tcp_build_timestamp_option(pcb, opts);
+ opts += 3;
+ }
+#endif
+
+ /* Set retransmission timer running if it is not currently enabled
+ This must be set before checking the route. */
+ if (pcb->rtime == -1) {
+ pcb->rtime = 0;
+ }
+
+ /* If we don't have a local IP address, we get one by
+ calling ip_route(). */
+ if (ip_addr_isany(&(pcb->local_ip))) {
+ netif = ip_route(&(pcb->remote_ip));
+ if (netif == NULL) {
+ return;
+ }
+ ip_addr_copy(pcb->local_ip, netif->ip_addr);
+ }
+
+ if (pcb->rttest == 0) {
+ pcb->rttest = tcp_ticks;
+ pcb->rtseq = ntohl(seg->tcphdr->seqno);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+ }
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+ htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
+ seg->len));
+
+ len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+
+ seg->p->len -= len;
+ seg->p->tot_len -= len;
+
+ seg->p->payload = seg->tcphdr;
+
+ seg->tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+#if TCP_CHECKSUM_ON_COPY
+ {
+ u32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
+ &(pcb->remote_ip),
+ IP_PROTO_TCP, seg->p->tot_len);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+ LWIP_ASSERT("data included but not checksummed",
+ seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
+ }
+
+ /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+ acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip),
+ &(pcb->remote_ip),
+ IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4);
+ /* add payload checksum */
+ if (seg->chksum_swapped) {
+ seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+ seg->chksum_swapped = 0;
+ }
+ acc += (u16_t)~(seg->chksum);
+ seg->tcphdr->chksum = FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ if (chksum_slow != seg->tcphdr->chksum) {
+ LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+ seg->tcphdr->chksum, chksum_slow));
+ seg->tcphdr->chksum = chksum_slow;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ }
+#else /* TCP_CHECKSUM_ON_COPY */
+ seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
+ &(pcb->remote_ip),
+ IP_PROTO_TCP, seg->p->tot_len);
+#endif /* TCP_CHECKSUM_ON_COPY */
+#endif /* CHECKSUM_GEN_TCP */
+ TCP_STATS_INC(tcp.xmit);
+
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) either to
+ * abort a connection or to show that there is no matching local connection
+ * for a received segment.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
+ * matching local pcb was found), tcp_listen_input() (if incoming segment
+ * has ACK flag set) and tcp_process() (received segment in the wrong state)
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst(u32_t seqno, u32_t ackno,
+ ip_addr_t *local_ip, ip_addr_t *remote_ip,
+ u16_t local_port, u16_t remote_port)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= sizeof(struct tcp_hdr)));
+
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = htons(local_port);
+ tcphdr->dest = htons(remote_port);
+ tcphdr->seqno = htonl(seqno);
+ tcphdr->ackno = htonl(ackno);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+ tcphdr->wnd = PP_HTONS(TCP_WND);
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
+ IP_PROTO_TCP, p->tot_len);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+ snmp_inc_tcpoutrsts();
+ /* Send output with hardcoded TTL since we have no access to the pcb */
+ ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
+ pbuf_free(p);
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move all unacked segments to the head of the unsent queue */
+ for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+ /* concatenate unsent queue after unacked queue */
+ seg->next = pcb->unsent;
+ /* unsent queue is the concatenated queue (of unacked, unsent) */
+ pcb->unsent = pcb->unacked;
+ /* unacked queue is now empty */
+ pcb->unacked = NULL;
+
+ /* increment number of retransmissions */
+ ++pcb->nrtx;
+
+ /* Don't take any RTT measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission */
+ tcp_output(pcb);
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retramsmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+ struct tcp_seg **cur_seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move the first unacked segment to the unsent queue */
+ /* Keep the unsent queue sorted. */
+ seg = pcb->unacked;
+ pcb->unacked = seg->next;
+
+ cur_seg = &(pcb->unsent);
+ while (*cur_seg &&
+ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = *cur_seg;
+ *cur_seg = seg;
+
+ ++pcb->nrtx;
+
+ /* Don't take any rtt measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission. */
+ snmp_inc_tcpretranssegs();
+ /* No need to call tcp_output: we are always called from tcp_input()
+ and thus tcp_output directly returns. */
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+ if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+ /* This is fast retransmit. Retransmit the first unacked segment. */
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: dupacks %"U16_F" (%"U32_F
+ "), fast retransmit %"U32_F"\n",
+ (u16_t)pcb->dupacks, pcb->lastack,
+ ntohl(pcb->unacked->tcphdr->seqno)));
+ tcp_rexmit(pcb);
+
+ /* Set ssthresh to half of the minimum of the current
+ * cwnd and the advertised window */
+ if (pcb->cwnd > pcb->snd_wnd) {
+ pcb->ssthresh = pcb->snd_wnd / 2;
+ } else {
+ pcb->ssthresh = pcb->cwnd / 2;
+ }
+
+ /* The minimum value for ssthresh should be 2 MSS */
+ if (pcb->ssthresh < 2*pcb->mss) {
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: The minimum value for ssthresh %"U16_F
+ " should be min 2 mss %"U16_F"...\n",
+ pcb->ssthresh, 2*pcb->mss));
+ pcb->ssthresh = 2*pcb->mss;
+ }
+
+ pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+ pcb->flags |= TF_INFR;
+ }
+}
+
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+void
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+ p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
+ if(p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_keepalive: could not allocate memory for pbuf\n"));
+ return;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
+ IP_PROTO_TCP, p->tot_len);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
+ &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+void
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ struct tcp_seg *seg;
+ u16_t len;
+ u8_t is_fin;
+
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
+ U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_zero_window_probe: tcp_ticks %"U32_F
+ " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+ seg = pcb->unacked;
+
+ if(seg == NULL) {
+ seg = pcb->unsent;
+ }
+ if(seg == NULL) {
+ return;
+ }
+
+ is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+ /* we want to send one seqno: either FIN or data (no options) */
+ len = is_fin ? 0 : 1;
+
+ p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
+ if(p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+ return;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+ if (is_fin) {
+ /* FIN segment, no data */
+ TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+ } else {
+ /* Data segment, copy in one byte from the head of the unacked queue */
+ struct tcp_hdr *thdr = (struct tcp_hdr *)seg->p->payload;
+ char *d = ((char *)p->payload + TCP_HLEN);
+ pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4);
+ }
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
+ IP_PROTO_TCP, p->tot_len);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
+ &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+ " ackno %"U32_F".\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+#endif /* LWIP_TCP */
diff --git a/core/lwip/src/core/timers.c b/core/lwip/src/core/timers.c
new file mode 100644
index 00000000..4e94f0d1
--- /dev/null
+++ b/core/lwip/src/core/timers.c
@@ -0,0 +1,483 @@
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/timers.h"
+#include "lwip/tcp_impl.h"
+
+#if LWIP_TIMERS
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/tcpip.h"
+
+#include "lwip/ip_frag.h"
+#include "netif/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout;
+#if NO_SYS
+static u32_t timeouts_last_time;
+#endif /* NO_SYS */
+
+#if LWIP_TCP
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static int tcpip_tcp_timer_active;
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_tcp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* call TCP timer handler */
+ tcp_tmr();
+ /* timer still needed? */
+ if (tcp_active_pcbs || tcp_tw_pcbs) {
+ /* restart timer */
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ } else {
+ /* disable timer */
+ tcpip_tcp_timer_active = 0;
+ }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void
+tcp_timer_needed(void)
+{
+ /* timer is off but needed again? */
+ if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+ /* enable and start timer */
+ tcpip_tcp_timer_active = 1;
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ }
+}
+#endif /* LWIP_TCP */
+
+#if IP_REASSEMBLY
+/**
+ * Timer callback function that calls ip_reass_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+ip_reass_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n"));
+ ip_reass_tmr();
+ sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
+}
+#endif /* IP_REASSEMBLY */
+
+#if LWIP_ARP
+/**
+ * Timer callback function that calls etharp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+arp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n"));
+ etharp_tmr();
+ undiarp_tmr();
+ sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+}
+#endif /* LWIP_ARP */
+
+#if LWIP_DHCP
+/**
+ * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_coarse(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
+ dhcp_coarse_tmr();
+ sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+}
+
+/**
+ * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_fine(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
+ dhcp_fine_tmr();
+ sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+}
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+/**
+ * Timer callback function that calls autoip_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+autoip_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
+ autoip_tmr();
+ sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+}
+#endif /* LWIP_AUTOIP */
+
+#if LWIP_IGMP
+/**
+ * Timer callback function that calls igmp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+igmp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
+ igmp_tmr();
+ sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Timer callback function that calls dns_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dns_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
+ dns_tmr();
+ sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+}
+#endif /* LWIP_DNS */
+
+/** Initialize this module */
+void sys_timeouts_init(void)
+{
+#if IP_REASSEMBLY
+ sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
+#endif /* IP_REASSEMBLY */
+#if LWIP_ARP
+ sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+#endif /* LWIP_ARP */
+#if LWIP_DHCP
+ sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+ sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+#endif /* LWIP_DNS */
+
+#if NO_SYS
+ /* Initialise timestamp for sys_check_timeouts */
+ timeouts_last_time = sys_now();
+#endif
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * - while waiting for a message using sys_timeouts_mbox_fetch()
+ * - by calling sys_check_timeouts() (NO_SYS==1 only)
+ *
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed
+ * @param arg argument to pass to the callback function
+ */
+#if LWIP_DEBUG_TIMERNAMES
+void
+sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void
+sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
+#endif /* LWIP_DEBUG_TIMERNAMES */
+{
+ struct sys_timeo *timeout, *t;
+
+ timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
+ if (timeout == NULL) {
+ LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
+ return;
+ }
+ timeout->next = NULL;
+ timeout->h = handler;
+ timeout->arg = arg;
+ timeout->time = msecs;
+#if LWIP_DEBUG_TIMERNAMES
+ timeout->handler_name = handler_name;
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
+ (void *)timeout, msecs, handler_name, (void *)arg));
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+ if (next_timeout == NULL) {
+ next_timeout = timeout;
+ return;
+ }
+
+ if (next_timeout->time > msecs) {
+ next_timeout->time -= msecs;
+ timeout->next = next_timeout;
+ next_timeout = timeout;
+ } else {
+ for(t = next_timeout; t != NULL; t = t->next) {
+ timeout->time -= t->time;
+ if (t->next == NULL || t->next->time > timeout->time) {
+ if (t->next != NULL) {
+ t->next->time -= timeout->time;
+ }
+ timeout->next = t->next;
+ t->next = timeout;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry, even though the timeout has not triggered yet.
+ *
+ * @note This function only works as expected if there is only one timeout
+ * calling 'handler' in the list of timeouts.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(sys_timeout_handler handler, void *arg)
+{
+ struct sys_timeo *prev_t, *t;
+
+ if (next_timeout == NULL) {
+ return;
+ }
+
+ for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
+ if ((t->h == handler) && (t->arg == arg)) {
+ /* We have a match */
+ /* Unlink from previous in list */
+ if (prev_t == NULL) {
+ next_timeout = t->next;
+ } else {
+ prev_t->next = t->next;
+ }
+ /* If not the last one, add time of this one back to next */
+ if (t->next != NULL) {
+ t->next->time += t->time;
+ }
+ memp_free(MEMP_SYS_TIMEOUT, t);
+ return;
+ }
+ }
+ return;
+}
+
+#if NO_SYS
+
+/** Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+void
+sys_check_timeouts(void)
+{
+ struct sys_timeo *tmptimeout;
+ u32_t diff;
+ sys_timeout_handler handler;
+ void *arg;
+ int had_one;
+ u32_t now;
+
+ now = sys_now();
+ if (next_timeout) {
+ /* this cares for wraparounds */
+ diff = LWIP_U32_DIFF(now, timeouts_last_time);
+ do
+ {
+ had_one = 0;
+ tmptimeout = next_timeout;
+ if (tmptimeout->time <= diff) {
+ /* timeout has expired */
+ had_one = 1;
+ timeouts_last_time = now;
+ diff -= tmptimeout->time;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+ handler(arg);
+ }
+ }
+ /* repeat until all expired timers have been called */
+ }while(had_one);
+ }
+}
+
+/** Set back the timestamp of the last call to sys_check_timeouts()
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+ timeouts_last_time = sys_now();
+}
+
+#else /* NO_SYS */
+
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+void
+sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+ u32_t time_needed;
+ struct sys_timeo *tmptimeout;
+ sys_timeout_handler handler;
+ void *arg;
+
+ again:
+ if (!next_timeout) {
+ time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
+ } else {
+ if (next_timeout->time > 0) {
+ time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
+ } else {
+ time_needed = SYS_ARCH_TIMEOUT;
+ }
+
+ if (time_needed == SYS_ARCH_TIMEOUT) {
+ /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
+ could be fetched. We should now call the timeout handler and
+ deallocate the memory allocated for the timeout. */
+ tmptimeout = next_timeout;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+ /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
+ timeout handler function. */
+ LOCK_TCPIP_CORE();
+ handler(arg);
+ UNLOCK_TCPIP_CORE();
+ }
+ LWIP_TCPIP_THREAD_ALIVE();
+
+ /* We try again to fetch a message from the mbox. */
+ goto again;
+ } else {
+ /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
+ occured. The time variable is set to the number of
+ milliseconds we waited for the message. */
+ if (time_needed < next_timeout->time) {
+ next_timeout->time -= time_needed;
+ } else {
+ next_timeout->time = 0;
+ }
+ }
+ }
+}
+
+#endif /* NO_SYS */
+
+#else /* LWIP_TIMERS */
+/* Satisfy the TCP code which calls this function */
+void
+tcp_timer_needed(void)
+{
+}
+#endif /* LWIP_TIMERS */
diff --git a/core/lwip/src/core/udp.c b/core/lwip/src/core/udp.c
new file mode 100644
index 00000000..4596ba2b
--- /dev/null
+++ b/core/lwip/src/core/udp.c
@@ -0,0 +1,966 @@
+/**
+ * @file
+ * User Datagram Protocol module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
+/* udp.c
+ *
+ * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).
+ *
+ */
+
+/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "arch/perf.h"
+#include "lwip/dhcp.h"
+
+#include <string.h>
+
+/* The list of UDP PCBs */
+/* exported in udp.h (was static) */
+struct udp_pcb *udp_pcbs;
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
+ * recv function. If no pcb is found or the datagram is incorrect, the
+ * pbuf is freed.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB.
+ * @param inp network interface on which the datagram was received.
+ *
+ */
+void
+udp_input(struct pbuf *p, struct netif *inp)
+{
+ struct udp_hdr *udphdr;
+ struct udp_pcb *pcb, *prev;
+ struct udp_pcb *uncon_pcb;
+ struct ip_hdr *iphdr;
+ u16_t src, dest;
+ u8_t local_match;
+ u8_t broadcast;
+
+ PERF_START;
+
+ UDP_STATS_INC(udp.recv);
+
+ iphdr = (struct ip_hdr *)p->payload;
+
+ /* Check minimum length (IP header + UDP header)
+ * and move payload pointer to UDP header */
+ if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) {
+ /* drop short packets */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+ UDP_STATS_INC(udp.lenerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+
+ udphdr = (struct udp_hdr *)p->payload;
+
+ /* is broadcast packet ? */
+ broadcast = ip_addr_isbroadcast(&current_iphdr_dest, inp);
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+ /* convert src and dest ports to host byte order */
+ src = ntohs(udphdr->src);
+ dest = ntohs(udphdr->dest);
+
+ udp_debug_print(udphdr);
+
+ /* print the UDP source and destination */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- "
+ "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
+ ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest),
+ ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src)));
+
+#if LWIP_DHCP
+ pcb = NULL;
+ /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
+ the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
+ if (dest == DHCP_CLIENT_PORT) {
+ /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
+ if (src == DHCP_SERVER_PORT) {
+ if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
+ /* accept the packe if
+ (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
+ - inp->dhcp->pcb->remote == ANY or iphdr->src */
+ if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) ||
+ ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &current_iphdr_src))) {
+ pcb = inp->dhcp->pcb;
+ }
+ }
+ }
+ } else
+#endif /* LWIP_DHCP */
+ {
+ prev = NULL;
+ local_match = 0;
+ uncon_pcb = NULL;
+ /* Iterate through the UDP pcb list for a matching pcb.
+ * 'Perfect match' pcbs (connected to the remote port & ip address) are
+ * preferred. If no perfect match is found, the first unconnected pcb that
+ * matches the local port and ip address gets the datagram. */
+ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+ local_match = 0;
+ /* print the PCB local and remote address */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- "
+ "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
+ ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
+ ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port,
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port));
+
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((pcb->local_port == dest) &&
+ ((!broadcast && ip_addr_isany(&pcb->local_ip)) ||
+ ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest) ||
+#if LWIP_IGMP
+ ip_addr_ismulticast(&current_iphdr_dest) ||
+#endif /* LWIP_IGMP */
+#if IP_SOF_BROADCAST_RECV
+ (broadcast && (pcb->so_options & SOF_BROADCAST)))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast))) {
+#endif /* IP_SOF_BROADCAST_RECV */
+ local_match = 1;
+ if ((uncon_pcb == NULL) &&
+ ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
+ /* the first unconnected matching PCB */
+ uncon_pcb = pcb;
+ }
+ }
+ /* compare PCB remote addr+port to UDP source addr+port */
+ if ((local_match != 0) &&
+ (pcb->remote_port == src) &&
+ (ip_addr_isany(&pcb->remote_ip) ||
+ ip_addr_cmp(&(pcb->remote_ip), &current_iphdr_src))) {
+ /* the first fully matching PCB */
+ if (prev != NULL) {
+ /* move the pcb to the front of udp_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ } else {
+ UDP_STATS_INC(udp.cachehit);
+ }
+ break;
+ }
+ prev = pcb;
+ }
+ /* no fully matching pcb found? then look for an unconnected pcb */
+ if (pcb == NULL) {
+ pcb = uncon_pcb;
+ }
+ }
+
+ /* Check checksum if this is a match or if it was directed at us. */
+ if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &current_iphdr_dest)) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
+#if LWIP_UDPLITE
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {
+ /* Do the UDP Lite checksum */
+#if CHECKSUM_CHECK_UDP
+ u16_t chklen = ntohs(udphdr->len);
+ if (chklen < sizeof(struct udp_hdr)) {
+ if (chklen == 0) {
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet (See RFC 3828 chap. 3.1) */
+ chklen = p->tot_len;
+ } else {
+ /* At least the UDP-Lite header must be covered by the
+ checksum! (Again, see RFC 3828 chap. 3.1) */
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ }
+ if (inet_chksum_pseudo_partial(p, &current_iphdr_src, &current_iphdr_dest,
+ IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_input: UDP Lite datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+#endif /* CHECKSUM_CHECK_UDP */
+ } else
+#endif /* LWIP_UDPLITE */
+ {
+#if CHECKSUM_CHECK_UDP
+ if (udphdr->chksum != 0) {
+ if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+ IP_PROTO_UDP, p->tot_len) != 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_input: UDP datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ }
+#endif /* CHECKSUM_CHECK_UDP */
+ }
+ if(pbuf_header(p, -UDP_HLEN)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ if (pcb != NULL) {
+ snmp_inc_udpindatagrams();
+#if SO_REUSE && SO_REUSE_RXTOALL
+ if ((broadcast || ip_addr_ismulticast(&current_iphdr_dest)) &&
+ ((pcb->so_options & SOF_REUSEADDR) != 0)) {
+ /* pass broadcast- or multicast packets to all multicast pcbs
+ if SOF_REUSEADDR is set on the first match */
+ struct udp_pcb *mpcb;
+ u8_t p_header_changed = 0;
+ for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
+ if (mpcb != pcb) {
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((mpcb->local_port == dest) &&
+ ((!broadcast && ip_addr_isany(&mpcb->local_ip)) ||
+ ip_addr_cmp(&(mpcb->local_ip), &current_iphdr_dest) ||
+#if LWIP_IGMP
+ ip_addr_ismulticast(&current_iphdr_dest) ||
+#endif /* LWIP_IGMP */
+#if IP_SOF_BROADCAST_RECV
+ (broadcast && (mpcb->so_options & SOF_BROADCAST)))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast))) {
+#endif /* IP_SOF_BROADCAST_RECV */
+ /* pass a copy of the packet to all local matches */
+ if (mpcb->recv != NULL) {
+ struct pbuf *q;
+ /* for that, move payload to IP header again */
+ if (p_header_changed == 0) {
+ pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
+ p_header_changed = 1;
+ }
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if (q != NULL) {
+ err_t err = pbuf_copy(q, p);
+ if (err == ERR_OK) {
+ /* move payload to UDP data */
+ pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
+ mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (p_header_changed) {
+ /* and move payload to UDP data again */
+ pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
+ }
+ }
+#endif /* SO_REUSE && SO_REUSE_RXTOALL */
+ /* callback */
+ if (pcb->recv != NULL) {
+ /* now the recv function is responsible for freeing p */
+ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
+ } else {
+ /* no recv function registered? then we have to free the pbuf! */
+ pbuf_free(p);
+ goto end;
+ }
+ } else {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
+
+#if LWIP_ICMP
+ /* No match was found, send ICMP destination port unreachable unless
+ destination address was broadcast/multicast. */
+ if (!broadcast &&
+ !ip_addr_ismulticast(&current_iphdr_dest)) {
+ /* move payload pointer back to ip header */
+ pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN);
+ LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr));
+ icmp_dest_unreach(p, ICMP_DUR_PORT);
+ }
+#endif /* LWIP_ICMP */
+ UDP_STATS_INC(udp.proterr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpnoports();
+ pbuf_free(p);
+ }
+ } else {
+ pbuf_free(p);
+ }
+end:
+ PERF_STOP("udp_input");
+}
+
+/**
+ * Send data using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ *
+ * The datagram will be sent to the current remote_ip & remote_port
+ * stored in pcb. If the pcb is not bound to a port, it will
+ * automatically be bound to a random port.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ * - More errors could be returned by lower protocol layers.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/** Same as udp_send() but with checksum
+ */
+err_t
+udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum)
+{
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port,
+ have_chksum, chksum);
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+/**
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port)
+{
+#if LWIP_CHECKSUM_ON_COPY
+ return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
+}
+
+/** Same as udp_sendto(), but with checksum */
+err_t
+udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+ u16_t dst_port, u8_t have_chksum, u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ struct netif *netif;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
+
+ /* find the outgoing network interface for this packet */
+#if LWIP_IGMP
+ netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip));
+#else
+ netif = ip_route(dst_ip);
+#endif /* LWIP_IGMP */
+
+ /* no outgoing network interface could be found? */
+ if (netif == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip)));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+#if LWIP_CHECKSUM_ON_COPY
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY */
+ return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+}
+
+/**
+ * Send data to a specified address using UDP.
+ * The netif used for sending can be specified.
+ *
+ * This function exists mainly for DHCP, to be able to send UDP packets
+ * on a netif that is still down.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ * @param netif the netif used for sending.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+{
+#if LWIP_CHECKSUM_ON_COPY
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
+}
+
+/** Same as udp_sendto_if(), but with checksum */
+err_t
+udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+ u16_t dst_port, struct netif *netif, u8_t have_chksum,
+ u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ struct udp_hdr *udphdr;
+ ip_addr_t *src_ip;
+ err_t err;
+ struct pbuf *q; /* q will be sent down the stack */
+
+#if IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ return ERR_VAL;
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ /* if the PCB is not yet bound to a port, bind it here */
+ if (pcb->local_port == 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
+ err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
+ return err;
+ }
+ }
+
+ /* not enough space to add an UDP header to first pbuf in given p chain? */
+ if (pbuf_header(p, UDP_HLEN)) {
+ /* allocate header in a separate new pbuf */
+ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p (only if p contains data) */
+ pbuf_chain(q, p);
+ }
+ /* first pbuf q points to header pbuf */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* adding space for header within p succeeded */
+ /* first pbuf q equals given pbuf */
+ q = p;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
+ (q->len >= sizeof(struct udp_hdr)));
+ /* q now represents the packet to be sent */
+ udphdr = (struct udp_hdr *)q->payload;
+ udphdr->src = htons(pcb->local_port);
+ udphdr->dest = htons(dst_port);
+ /* in UDP, 0 checksum means 'no checksum' */
+ udphdr->chksum = 0x0000;
+
+ /* Multicast Loop? */
+#if LWIP_IGMP
+ if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) {
+ q->flags |= PBUF_FLAG_MCASTLOOP;
+ }
+#endif /* LWIP_IGMP */
+
+
+ /* PCB local address is IP_ANY_ADDR? */
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = &(netif->ip_addr);
+ } else {
+ /* check if UDP PCB local IP address is correct
+ * this could be an old address if netif->ip_addr has changed */
+ if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) {
+ /* local_ip doesn't match, drop the packet */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ q = NULL;
+ /* p is still referenced by the caller, and will live on */
+ }
+ return ERR_VAL;
+ }
+ /* use UDP PCB local IP address as source address */
+ src_ip = &(pcb->local_ip);
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+#if LWIP_UDPLITE
+ /* UDP Lite protocol? */
+ if (pcb->flags & UDP_FLAGS_UDPLITE) {
+ u16_t chklen, chklen_hdr;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+ /* set UDP message length in UDP header */
+ chklen_hdr = chklen = pcb->chksum_len_tx;
+ if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
+ if (chklen != 0) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
+ }
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet. (See RFC 3828 chap. 3.1)
+ At least the UDP-Lite header must be covered by the
+ checksum, therefore, if chksum_len has an illegal
+ value, we generate the checksum over the complete
+ packet to be safe. */
+ chklen_hdr = 0;
+ chklen = q->tot_len;
+ }
+ udphdr->len = htons(chklen_hdr);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip,
+ IP_PROTO_UDPLITE, q->tot_len,
+#if !LWIP_CHECKSUM_ON_COPY
+ chklen);
+#else /* !LWIP_CHECKSUM_ON_COPY */
+ (have_chksum ? UDP_HLEN : chklen));
+ if (have_chksum) {
+ u32_t acc;
+ acc = udphdr->chksum + (u16_t)~(chksum);
+ udphdr->chksum = FOLD_U32T(acc);
+ }
+#endif /* !LWIP_CHECKSUM_ON_COPY */
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udphdr->chksum == 0x0000) {
+ udphdr->chksum = 0xffff;
+ }
+#endif /* CHECKSUM_GEN_UDP */
+ /* output to IP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n"));
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ } else
+#endif /* LWIP_UDPLITE */
+ { /* UDP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+ udphdr->len = htons(q->tot_len);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+ u16_t udpchksum;
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ u32_t acc;
+ udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP,
+ q->tot_len, UDP_HLEN);
+ acc = udpchksum + (u16_t)~(chksum);
+ udpchksum = FOLD_U32T(acc);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len);
+ }
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udpchksum == 0x0000) {
+ udpchksum = 0xffff;
+ }
+ udphdr->chksum = udpchksum;
+ }
+#endif /* CHECKSUM_GEN_UDP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n"));
+ /* output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ }
+ /* TODO: must this be increased even if error occured? */
+ snmp_inc_udpoutdatagrams();
+
+ /* did we chain a separate header pbuf earlier? */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ q = NULL;
+ /* p is still referenced by the caller, and will live on */
+ }
+
+ UDP_STATS_INC(udp.xmit);
+ return err;
+}
+
+/**
+ * Bind an UDP PCB.
+ *
+ * @param pcb UDP PCB to be bound with a local address ipaddr and port.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with. Use 0 to automatically bind
+ * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * UDP_LOCAL_PORT_RANGE_END.
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified ipaddr and port are already bound to by
+ * another UDP PCB.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+ u8_t rebind;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
+ ip_addr_debug_print(UDP_DEBUG, ipaddr);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
+
+ rebind = 0;
+ /* Check for double bind and rebind of the same pcb */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ /* is this UDP PCB already on active list? */
+ if (pcb == ipcb) {
+ /* pcb may occur at most once in active list */
+ LWIP_ASSERT("rebind == 0", rebind == 0);
+ /* pcb already in list, just rebind */
+ rebind = 1;
+ }
+
+ /* By default, we don't allow to bind to a port that any other udp
+ PCB is alread bound to, unless *all* PCBs with that port have tha
+ REUSEADDR flag set. */
+#if SO_REUSE
+ else if (((pcb->so_options & SOF_REUSEADDR) == 0) &&
+ ((ipcb->so_options & SOF_REUSEADDR) == 0)) {
+#else /* SO_REUSE */
+ /* port matches that of PCB in list and REUSEADDR not set -> reject */
+ else {
+#endif /* SO_REUSE */
+ if ((ipcb->local_port == port) &&
+ /* IP address matches, or one is IP_ADDR_ANY? */
+ (ip_addr_isany(&(ipcb->local_ip)) ||
+ ip_addr_isany(ipaddr) ||
+ ip_addr_cmp(&(ipcb->local_ip), ipaddr))) {
+ /* other PCB already binds to this local IP and port */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
+ return ERR_USE;
+ }
+ }
+ }
+
+ ip_addr_set(&pcb->local_ip, ipaddr);
+
+ /* no port specified? */
+ if (port == 0) {
+#ifndef UDP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+ "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define UDP_LOCAL_PORT_RANGE_START 0xc000
+#define UDP_LOCAL_PORT_RANGE_END 0xffff
+#endif
+ port = UDP_LOCAL_PORT_RANGE_START;
+ ipcb = udp_pcbs;
+ while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {
+ if (ipcb->local_port == port) {
+ /* port is already used by another udp_pcb */
+ port++;
+ /* restart scanning all udp pcbs */
+ ipcb = udp_pcbs;
+ } else {
+ /* go on with next udp pcb */
+ ipcb = ipcb->next;
+ }
+ }
+ if (ipcb != NULL) {
+ /* no more ports available in local range */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+ return ERR_USE;
+ }
+ }
+ pcb->local_port = port;
+ snmp_insert_udpidx_tree(pcb);
+ /* pcb not active yet? */
+ if (rebind == 0) {
+ /* place the PCB on the active list if not already there */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ }
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n",
+ ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
+ ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip),
+ pcb->local_port));
+ return ERR_OK;
+}
+/**
+ * Connect an UDP PCB.
+ *
+ * This will associate the UDP PCB with the remote address.
+ *
+ * @param pcb UDP PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ * @param port remote UDP port to connect with.
+ *
+ * @return lwIP error code
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * The udp pcb is bound to a random local port if not already bound.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+
+ if (pcb->local_port == 0) {
+ err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+ if (err != ERR_OK) {
+ return err;
+ }
+ }
+
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ pcb->remote_port = port;
+ pcb->flags |= UDP_FLAGS_CONNECTED;
+/** TODO: this functionality belongs in upper layers */
+#ifdef LWIP_UDP_TODO
+ /* Nail down local IP for netconn_addr()/getsockname() */
+ if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) {
+ struct netif *netif;
+
+ if ((netif = ip_route(&(pcb->remote_ip))) == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+ /** TODO: this will bind the udp pcb locally, to the interface which
+ is used to route output packets to the remote address. However, we
+ might want to accept incoming packets on any interface! */
+ pcb->local_ip = netif->ip_addr;
+ } else if (ip_addr_isany(&pcb->remote_ip)) {
+ pcb->local_ip.addr = 0;
+ }
+#endif
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n",
+ ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
+ ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip),
+ pcb->local_port));
+
+ /* Insert UDP PCB into the list of active UDP PCBs. */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ if (pcb == ipcb) {
+ /* already on the list, just return */
+ return ERR_OK;
+ }
+ }
+ /* PCB not yet on the list, add PCB now */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ return ERR_OK;
+}
+
+/**
+ * Disconnect a UDP PCB
+ *
+ * @param pcb the udp pcb to disconnect.
+ */
+void
+udp_disconnect(struct udp_pcb *pcb)
+{
+ /* reset remote address association */
+ ip_addr_set_any(&pcb->remote_ip);
+ pcb->remote_port = 0;
+ /* mark PCB as unconnected */
+ pcb->flags &= ~UDP_FLAGS_CONNECTED;
+}
+
+/**
+ * Set a receive callback for a UDP PCB
+ *
+ * This callback will be called when receiving a datagram for the pcb.
+ *
+ * @param pcb the pcb for wich to set the recv callback
+ * @param recv function pointer of the callback function
+ * @param recv_arg additional argument to pass to the callback function
+ */
+void
+udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Remove an UDP PCB.
+ *
+ * @param pcb UDP PCB to be removed. The PCB is removed from the list of
+ * UDP PCB's and the data structure is freed from memory.
+ *
+ * @see udp_new()
+ */
+void
+udp_remove(struct udp_pcb *pcb)
+{
+ struct udp_pcb *pcb2;
+
+ snmp_delete_udpidx_tree(pcb);
+ /* pcb to be removed is first in list? */
+ if (udp_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ udp_pcbs = udp_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in udp_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ }
+ }
+ }
+ memp_free(MEMP_UDP_PCB, pcb);
+}
+
+/**
+ * Create a UDP PCB.
+ *
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new(void)
+{
+ struct udp_pcb *pcb;
+ pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
+ /* could allocate UDP PCB? */
+ if (pcb != NULL) {
+ /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
+ * which means checksum is generated over the whole datagram per default
+ * (recommended as default by RFC 3828). */
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct udp_pcb));
+ pcb->ttl = UDP_TTL;
+ }
+ return pcb;
+}
+
+#if UDP_DEBUG
+/**
+ * Print UDP header information for debug purposes.
+ *
+ * @param udphdr pointer to the udp header in memory.
+ */
+void
+udp_debug_print(struct udp_hdr *udphdr)
+{
+ LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ ntohs(udphdr->src), ntohs(udphdr->dest)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n",
+ ntohs(udphdr->len), ntohs(udphdr->chksum)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */
diff --git a/core/lwip/src/include/arch/cc.h b/core/lwip/src/include/arch/cc.h
new file mode 100644
index 00000000..5ab13dec
--- /dev/null
+++ b/core/lwip/src/include/arch/cc.h
@@ -0,0 +1,50 @@
+#ifndef __LWIP_ARCH_CC_H__
+#define __LWIP_ARCH_CC_H__
+
+#include <klibc/compiler.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <kaboom.h>
+#include <stdio.h>
+
+#define BYTE_ORDER LITTLE_ENDIAN
+
+typedef uint8_t u8_t;
+typedef int8_t s8_t;
+typedef uint16_t u16_t;
+typedef int16_t s16_t;
+typedef uint32_t u32_t;
+typedef int32_t s32_t;
+
+typedef uintptr_t mem_ptr_t;
+
+#define PACK_STRUCT_STRUCT __packed
+
+#define LWIP_PLATFORM_USE_DPRINTF
+
+#ifdef LWIP_PLATFORM_USE_DPRINTF
+# include <dprintf.h>
+# define LWIP_PLATFORM_PRINTF dprintf
+#else
+# define LWIP_PLATFORM_PRINTF printf
+#endif
+
+
+#if 1
+#define LWIP_PLATFORM_DIAG(x) do { LWIP_PLATFORM_PRINTF x; } while(0)
+#define LWIP_PLATFORM_ASSERT(x) do { LWIP_PLATFORM_PRINTF("LWIP(%s,%d,%p): %s", __FILE__, __LINE__, __builtin_return_address(0), (x)); kaboom(); } while(0)
+#else
+#define LWIP_PLATFORM_DIAG(x) ((void)0) /* For now... */
+#define LWIP_PLATFORM_ASSERT(x) kaboom()
+#endif
+
+#define U16_F PRIu16
+#define S16_F PRId16
+#define X16_F PRIx16
+#define U32_F PRIu16
+#define S32_F PRId16
+#define X32_F PRIx16
+#define SZT_F "zu"
+
+#endif /* __LWIP_ARCH_CC_H__ */
diff --git a/core/lwip/src/include/arch/perf.h b/core/lwip/src/include/arch/perf.h
new file mode 100644
index 00000000..4ceb850c
--- /dev/null
+++ b/core/lwip/src/include/arch/perf.h
@@ -0,0 +1,7 @@
+#ifndef __LWIP_ARCH_PERF_H__
+#define __LWIP_ARCH_PERF_H__
+
+#define PERF_START
+#define PERF_STOP(x)
+
+#endif /* __LWIP_ARCH_PERF_H__ */
diff --git a/core/lwip/src/include/arch/sys_arch.h b/core/lwip/src/include/arch/sys_arch.h
new file mode 100644
index 00000000..732a19c4
--- /dev/null
+++ b/core/lwip/src/include/arch/sys_arch.h
@@ -0,0 +1,85 @@
+#ifndef __LWIP_ARCH_SYS_ARCH_H__
+#define __LWIP_ARCH_SYS_ARCH_H__
+
+#include <stddef.h>
+#include "arch/cc.h"
+#include <thread.h>
+#include <mbox.h>
+
+typedef struct semaphore *sys_sem_t;
+typedef struct mailbox *sys_mbox_t;
+typedef struct thread *sys_thread_t;
+
+static inline void sys_sem_signal(sys_sem_t *sem)
+{
+ if (!!sem)
+ sem_up(*sem);
+}
+
+#define sys_now ms_timer
+
+#define SYS_MBOX_NULL NULL
+#define SYS_SEM_NULL NULL
+
+extern void __compile_time_error(void);
+
+#define SYS_ARCH_OP(var, val, inc, add) \
+do { \
+ if (__builtin_constant_p(val) && (val) == 1) { \
+ switch (sizeof(var)) { \
+ case 1: \
+ asm volatile(inc "b %0" : "+m" (var)); \
+ break; \
+ case 2: \
+ asm volatile(inc "w %0" : "+m" (var)); \
+ break; \
+ case 4: \
+ asm volatile(inc "l %0" : "+m" (var)); \
+ break; \
+ default: \
+ __compile_time_error(); \
+ break; \
+ } \
+ } else { \
+ switch (sizeof(var)) { \
+ case 1: \
+ asm volatile(add "b %1,%0" : "+m" (var) : "ri" (val)); \
+ break; \
+ case 2: \
+ asm volatile(add "w %1,%0" : "+m" (var) : "ri" (val)); \
+ break; \
+ case 4: \
+ asm volatile(add "l %1,%0" : "+m" (var) : "ri" (val)); \
+ break; \
+ default: \
+ __compile_time_error(); \
+ break; \
+ } \
+ } \
+} while (0)
+
+static inline struct sys_timeouts *sys_arch_timeouts(void)
+{
+ return (struct sys_timeouts *)&current()->pvt;
+}
+
+#define SYS_ARCH_INC(var, val) SYS_ARCH_OP(var, val, "inc", "add")
+#define SYS_ARCH_DEC(var, val) SYS_ARCH_OP(var, val, "dec", "sub")
+
+#define SYS_ARCH_GET(var, ret) \
+ do { \
+ volatile __typeof__(var) * const __varp = &(var); \
+ ret = *__varp; \
+ } while (0)
+
+#define SYS_ARCH_SET(var, val) \
+ do { \
+ volatile __typeof__(var) * const __varp = &(var); \
+ *__varp = val; \
+ } while (0)
+
+#define SYS_ARCH_DECL_PROTECT(VAR) irq_state_t VAR
+#define SYS_ARCH_PROTECT(VAR) VAR = irq_save()
+#define SYS_ARCH_UNPROTECT(VAR) irq_restore(VAR)
+
+#endif /* __LWIP_ARCH_SYS_ARCH_H__ */
diff --git a/core/lwip/src/include/ipv4/lwip/autoip.h b/core/lwip/src/include/ipv4/lwip/autoip.h
new file mode 100644
index 00000000..23c264a1
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/autoip.h
@@ -0,0 +1,119 @@
+/**
+ * @file
+ *
+ * AutoIP Automatic LinkLocal IP Configuration
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ *
+ * Please coordinate changes and requests with Dominik Spies
+ * <kontakt@dspies.de>
+ */
+
+#ifndef __LWIP_AUTOIP_H__
+#define __LWIP_AUTOIP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "netif/etharp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* AutoIP Timing */
+#define AUTOIP_TMR_INTERVAL 100
+#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL)
+
+/* RFC 3927 Constants */
+#define PROBE_WAIT 1 /* second (initial random delay) */
+#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */
+#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */
+#define PROBE_NUM 3 /* (number of probe packets) */
+#define ANNOUNCE_NUM 2 /* (number of announcement packets) */
+#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */
+#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */
+#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */
+#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */
+#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */
+
+/* AutoIP client states */
+#define AUTOIP_STATE_OFF 0
+#define AUTOIP_STATE_PROBING 1
+#define AUTOIP_STATE_ANNOUNCING 2
+#define AUTOIP_STATE_BOUND 3
+
+struct autoip
+{
+ ip_addr_t llipaddr; /* the currently selected, probed, announced or used LL IP-Address */
+ u8_t state; /* current AutoIP state machine state */
+ u8_t sent_num; /* sent number of probes or announces, dependent on state */
+ u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */
+ u8_t lastconflict; /* ticks until a conflict can be solved by defending */
+ u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */
+};
+
+
+/** Init srand, has to be called before entering mainloop */
+void autoip_init(void);
+
+/** Set a struct autoip allocated by the application to work with */
+void autoip_set_struct(struct netif *netif, struct autoip *autoip);
+
+/** Start AutoIP client */
+err_t autoip_start(struct netif *netif);
+
+/** Stop AutoIP client */
+err_t autoip_stop(struct netif *netif);
+
+/** Handles every incoming ARP Packet, called by etharp_arp_input */
+void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
+
+/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */
+void autoip_tmr(void);
+
+/** Handle a possible change in the network configuration */
+void autoip_network_changed(struct netif *netif);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_AUTOIP */
+
+#endif /* __LWIP_AUTOIP_H__ */
diff --git a/core/lwip/src/include/ipv4/lwip/icmp.h b/core/lwip/src/include/ipv4/lwip/icmp.h
new file mode 100644
index 00000000..d47a7d8a
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/icmp.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ICMP_H__
+#define __LWIP_ICMP_H__
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ICMP_ER 0 /* echo reply */
+#define ICMP_DUR 3 /* destination unreachable */
+#define ICMP_SQ 4 /* source quench */
+#define ICMP_RD 5 /* redirect */
+#define ICMP_ECHO 8 /* echo */
+#define ICMP_TE 11 /* time exceeded */
+#define ICMP_PP 12 /* parameter problem */
+#define ICMP_TS 13 /* timestamp */
+#define ICMP_TSR 14 /* timestamp reply */
+#define ICMP_IRQ 15 /* information request */
+#define ICMP_IR 16 /* information reply */
+
+enum icmp_dur_type {
+ ICMP_DUR_NET = 0, /* net unreachable */
+ ICMP_DUR_HOST = 1, /* host unreachable */
+ ICMP_DUR_PROTO = 2, /* protocol unreachable */
+ ICMP_DUR_PORT = 3, /* port unreachable */
+ ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */
+ ICMP_DUR_SR = 5 /* source route failed */
+};
+
+enum icmp_te_type {
+ ICMP_TE_TTL = 0, /* time to live exceeded in transit */
+ ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */
+};
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+/** This is the standard ICMP header only that the u32_t data
+ * is splitted to two u16_t like ICMP echo needs it.
+ * This header is also used for other ICMP types that do not
+ * use the data part.
+ */
+PACK_STRUCT_BEGIN
+struct icmp_echo_hdr {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u16_t seqno);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define ICMPH_TYPE(hdr) ((hdr)->type)
+#define ICMPH_CODE(hdr) ((hdr)->code)
+
+/** Combines type and code to an u16_t */
+#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t))
+#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c))
+
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+void icmp_input(struct pbuf *p, struct netif *inp);
+void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
+void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
+
+#endif /* LWIP_ICMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_ICMP_H__ */
diff --git a/core/lwip/src/include/ipv4/lwip/igmp.h b/core/lwip/src/include/ipv4/lwip/igmp.h
new file mode 100644
index 00000000..8aabac24
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/igmp.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+#ifndef __LWIP_IGMP_H__
+#define __LWIP_IGMP_H__
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* IGMP timer */
+#define IGMP_TMR_INTERVAL 100 /* Milliseconds */
+#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL)
+#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL)
+
+/* MAC Filter Actions, these are passed to a netif's
+ * igmp_mac_filter callback function. */
+#define IGMP_DEL_MAC_FILTER 0
+#define IGMP_ADD_MAC_FILTER 1
+
+
+/**
+ * igmp group structure - there is
+ * a list of groups for each interface
+ * these should really be linked from the interface, but
+ * if we keep them separate we will not affect the lwip original code
+ * too much
+ *
+ * There will be a group for the all systems group address but this
+ * will not run the state machine as it is used to kick off reports
+ * from all the other groups
+ */
+struct igmp_group {
+ /** next link */
+ struct igmp_group *next;
+ /** interface on which the group is active */
+ struct netif *netif;
+ /** multicast address */
+ ip_addr_t group_address;
+ /** signifies we were the last person to report */
+ u8_t last_reporter_flag;
+ /** current state of the group */
+ u8_t group_state;
+ /** timer for reporting, negative is OFF */
+ u16_t timer;
+ /** counter of simultaneous uses */
+ u8_t use;
+};
+
+/* Prototypes */
+void igmp_init(void);
+err_t igmp_start(struct netif *netif);
+err_t igmp_stop(struct netif *netif);
+void igmp_report_groups(struct netif *netif);
+struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr);
+void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest);
+err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr);
+err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr);
+void igmp_tmr(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IGMP */
+
+#endif /* __LWIP_IGMP_H__ */
diff --git a/core/lwip/src/include/ipv4/lwip/inet.h b/core/lwip/src/include/ipv4/lwip/inet.h
new file mode 100644
index 00000000..0513c740
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/inet.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_INET_H__
+#define __LWIP_INET_H__
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** For compatibility with BSD code */
+#include <netinet/in.h>
+
+/** 255.255.255.255 */
+#define INADDR_NONE IPADDR_NONE
+/** 127.0.0.1 */
+#define INADDR_LOOPBACK IPADDR_LOOPBACK
+/** 0.0.0.0 */
+#define INADDR_ANY IPADDR_ANY
+/** 255.255.255.255 */
+#define INADDR_BROADCAST IPADDR_BROADCAST
+
+/* Definitions of the bits in an Internet address integer.
+
+ On subnets, host and network parts are found according to
+ the subnet mask, not these masks. */
+#define IN_CLASSA(a) IP_CLASSA(a)
+#define IN_CLASSA_NET IP_CLASSA_NET
+#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT
+#define IN_CLASSA_HOST IP_CLASSA_HOST
+#define IN_CLASSA_MAX IP_CLASSA_MAX
+
+#define IN_CLASSB(b) IP_CLASSB(b)
+#define IN_CLASSB_NET IP_CLASSB_NET
+#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT
+#define IN_CLASSB_HOST IP_CLASSB_HOST
+#define IN_CLASSB_MAX IP_CLASSB_MAX
+
+#define IN_CLASSC(c) IP_CLASSC(c)
+#define IN_CLASSC_NET IP_CLASSC_NET
+#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT
+#define IN_CLASSC_HOST IP_CLASSC_HOST
+#define IN_CLASSC_MAX IP_CLASSC_MAX
+
+#define IN_CLASSD(d) IP_CLASSD(d)
+#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */
+#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */
+#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */
+#define IN_CLASSD_MAX IP_CLASSD_MAX
+
+#define IN_MULTICAST(a) IP_MULTICAST(a)
+
+#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a)
+#define IN_BADCLASS(a) IP_BADCLASS(a)
+
+#define IN_LOOPBACKNET IP_LOOPBACKNET
+
+#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
+#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
+/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */
+#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr) ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr))
+
+/* directly map this to the lwip internal functions */
+#define inet_addr(cp) ipaddr_addr(cp)
+#define inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)addr)
+#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr))
+#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_INET_H__ */
diff --git a/core/lwip/src/include/ipv4/lwip/inet_chksum.h b/core/lwip/src/include/ipv4/lwip/inet_chksum.h
new file mode 100644
index 00000000..79a2d90f
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/inet_chksum.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_INET_CHKSUM_H__
+#define __LWIP_INET_CHKSUM_H__
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+/** Swap the bytes in an u16_t: much like htons() for little-endian */
+#ifndef SWAP_BYTES_IN_WORD
+#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)
+/* little endian and PLATFORM_BYTESWAP defined */
+#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w)
+#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */
+/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */
+#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8)
+#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/
+#endif /* SWAP_BYTES_IN_WORD */
+
+/** Split an u32_t in two u16_ts and add them up */
+#ifndef FOLD_U32T
+#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL))
+#endif
+
+#if LWIP_CHECKSUM_ON_COPY
+/** Function-like macro: same as MEMCPY but returns the checksum of copied data
+ as u16_t */
+#ifndef LWIP_CHKSUM_COPY
+#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len)
+#ifndef LWIP_CHKSUM_COPY_ALGORITHM
+#define LWIP_CHKSUM_COPY_ALGORITHM 1
+#endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+#endif /* LWIP_CHKSUM_COPY */
+#else /* LWIP_CHECKSUM_ON_COPY */
+#define LWIP_CHKSUM_COPY_ALGORITHM 0
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16_t inet_chksum(void *dataptr, u16_t len);
+u16_t inet_chksum_pbuf(struct pbuf *p);
+u16_t inet_chksum_pseudo(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len);
+u16_t inet_chksum_pseudo_partial(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len, u16_t chksum_len);
+#if LWIP_CHKSUM_COPY_ALGORITHM
+u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len);
+#endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_INET_H__ */
+
diff --git a/core/lwip/src/include/ipv4/lwip/ip.h b/core/lwip/src/include/ipv4/lwip/ip.h
new file mode 100644
index 00000000..74f501d1
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/ip.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_IP_H__
+#define __LWIP_IP_H__
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Currently, the function ip_output_if_opt() is only used with IGMP */
+#define IP_OPTIONS_SEND LWIP_IGMP
+
+#define IP_HLEN 20
+
+#define IP_PROTO_ICMP 1
+#define IP_PROTO_IGMP 2
+#define IP_PROTO_UDP 17
+#define IP_PROTO_UDPLITE 136
+#define IP_PROTO_TCP 6
+
+/* This is passed as the destination address to ip_output_if (not
+ to ip_output), meaning that an IP header already is constructed
+ in the pbuf. This is used when TCP retransmits. */
+#ifdef IP_HDRINCL
+#undef IP_HDRINCL
+#endif /* IP_HDRINCL */
+#define IP_HDRINCL NULL
+
+#if LWIP_NETIF_HWADDRHINT
+#define IP_PCB_ADDRHINT ;u8_t addr_hint
+#else
+#define IP_PCB_ADDRHINT
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+/* This is the common part of all PCB types. It needs to be at the
+ beginning of a PCB type definition. It is located here so that
+ changes to this common part are made in one location instead of
+ having to change all PCB structs. */
+#define IP_PCB \
+ /* ip addresses in network byte order */ \
+ ip_addr_t local_ip; \
+ ip_addr_t remote_ip; \
+ /* Socket options */ \
+ u8_t so_options; \
+ /* Type Of Service */ \
+ u8_t tos; \
+ /* Time To Live */ \
+ u8_t ttl \
+ /* link layer address resolution hint */ \
+ IP_PCB_ADDRHINT
+
+struct ip_pcb {
+/* Common members of all PCB types */
+ IP_PCB;
+};
+
+/*
+ * Option flags per-socket. These are the same like SO_XXX.
+ */
+/*#define SOF_DEBUG (u8_t)0x01U Unimplemented: turn on debugging info recording */
+#define SOF_ACCEPTCONN (u8_t)0x02U /* socket has had listen() */
+#define SOF_REUSEADDR (u8_t)0x04U /* allow local address reuse */
+#define SOF_KEEPALIVE (u8_t)0x08U /* keep connections alive */
+/*#define SOF_DONTROUTE (u8_t)0x10U Unimplemented: just use interface addresses */
+#define SOF_BROADCAST (u8_t)0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+/*#define SOF_USELOOPBACK (u8_t)0x40U Unimplemented: bypass hardware when possible */
+#define SOF_LINGER (u8_t)0x80U /* linger on close if data present */
+/*#define SOF_OOBINLINE (u16_t)0x0100U Unimplemented: leave received OOB data in line */
+/*#define SOF_REUSEPORT (u16_t)0x0200U Unimplemented: allow local address & port reuse */
+
+/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */
+#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/)
+
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_hdr {
+ /* version / header length / type of service */
+ PACK_STRUCT_FIELD(u16_t _v_hl_tos);
+ /* total length */
+ PACK_STRUCT_FIELD(u16_t _len);
+ /* identification */
+ PACK_STRUCT_FIELD(u16_t _id);
+ /* fragment offset field */
+ PACK_STRUCT_FIELD(u16_t _offset);
+#define IP_RF 0x8000U /* reserved fragment flag */
+#define IP_DF 0x4000U /* dont fragment flag */
+#define IP_MF 0x2000U /* more fragments flag */
+#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */
+ /* time to live */
+ PACK_STRUCT_FIELD(u8_t _ttl);
+ /* protocol*/
+ PACK_STRUCT_FIELD(u8_t _proto);
+ /* checksum */
+ PACK_STRUCT_FIELD(u16_t _chksum);
+ /* source and destination IP addresses */
+ PACK_STRUCT_FIELD(ip_addr_p_t src);
+ PACK_STRUCT_FIELD(ip_addr_p_t dest);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12)
+#define IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f)
+#define IPH_TOS(hdr) (ntohs((hdr)->_v_hl_tos) & 0xff)
+#define IPH_LEN(hdr) ((hdr)->_len)
+#define IPH_ID(hdr) ((hdr)->_id)
+#define IPH_OFFSET(hdr) ((hdr)->_offset)
+#define IPH_TTL(hdr) ((hdr)->_ttl)
+#define IPH_PROTO(hdr) ((hdr)->_proto)
+#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
+
+#define IPH_VHLTOS_SET(hdr, v, hl, tos) (hdr)->_v_hl_tos = (htons(((v) << 12) | ((hl) << 8) | (tos)))
+#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
+#define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
+#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
+#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl)
+#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto)
+#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
+
+/** The interface that provided the packet for the current callback invocation. */
+extern struct netif *current_netif;
+/** Header of the input packet currently being processed. */
+extern const struct ip_hdr *current_header;
+/** Source IP address of current_header */
+extern ip_addr_t current_iphdr_src;
+/** Destination IP address of current_header */
+extern ip_addr_t current_iphdr_dest;
+
+#define ip_init() /* Compatibility define, not init needed. */
+struct netif *ip_route(ip_addr_t *dest);
+err_t ip_input(struct pbuf *p, struct netif *inp);
+err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto);
+err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto,
+ struct netif *netif);
+#if LWIP_NETIF_HWADDRHINT
+err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT */
+#if IP_OPTIONS_SEND
+err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen);
+#endif /* IP_OPTIONS_SEND */
+/** Get the interface that received the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_netif() (current_netif)
+/** Get the IP header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_header() (current_header)
+/** Source IP address of current_header */
+#define ip_current_src_addr() (&current_iphdr_src)
+/** Destination IP address of current_header */
+#define ip_current_dest_addr() (&current_iphdr_dest)
+
+#if IP_DEBUG
+void ip_debug_print(struct pbuf *p);
+#else
+#define ip_debug_print(p)
+#endif /* IP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_H__ */
+
+
diff --git a/core/lwip/src/include/ipv4/lwip/ip_addr.h b/core/lwip/src/include/ipv4/lwip/ip_addr.h
new file mode 100644
index 00000000..77f84e02
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/ip_addr.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_IP_ADDR_H__
+#define __LWIP_IP_ADDR_H__
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the aligned version of ip_addr_t,
+ used as local variable, on the stack, etc. */
+struct ip_addr {
+ u32_t addr;
+};
+
+/* This is the packed version of ip_addr_t,
+ used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_addr_packed {
+ PACK_STRUCT_FIELD(u32_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** ip_addr_t uses a struct for convenience only, so that the same defines can
+ * operate both on ip_addr_t as well as on ip_addr_p_t. */
+typedef struct ip_addr ip_addr_t;
+typedef struct ip_addr_packed ip_addr_p_t;
+
+/*
+ * struct ipaddr2 is used in the definition of the ARP packet format in
+ * order to support compilers that don't have structure packing.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_addr2 {
+ PACK_STRUCT_FIELD(u16_t addrw[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Forward declaration to not include netif.h */
+struct netif;
+
+extern const ip_addr_t ip_addr_any;
+extern const ip_addr_t ip_addr_broadcast;
+
+/** IP_ADDR_ can be used as a fixed IP address
+ * for the wildcard and the broadcast address
+ */
+#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any)
+#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast)
+
+/** 255.255.255.255 */
+#define IPADDR_NONE ((u32_t)0xffffffffUL)
+/** 127.0.0.1 */
+#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL)
+/** 0.0.0.0 */
+#define IPADDR_ANY ((u32_t)0x00000000UL)
+/** 255.255.255.255 */
+#define IPADDR_BROADCAST ((u32_t)0xffffffffUL)
+
+/* Definitions of the bits in an Internet address integer.
+
+ On subnets, host and network parts are found according to
+ the subnet mask, not these masks. */
+#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0)
+#define IP_CLASSA_NET 0xff000000
+#define IP_CLASSA_NSHIFT 24
+#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET)
+#define IP_CLASSA_MAX 128
+
+#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL)
+#define IP_CLASSB_NET 0xffff0000
+#define IP_CLASSB_NSHIFT 16
+#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET)
+#define IP_CLASSB_MAX 65536
+
+#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL)
+#define IP_CLASSC_NET 0xffffff00
+#define IP_CLASSC_NSHIFT 8
+#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET)
+
+#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL)
+#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IP_MULTICAST(a) IP_CLASSD(a)
+
+#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+
+#define IP_LOOPBACKNET 127 /* official! */
+
+
+#if BYTE_ORDER == BIG_ENDIAN
+/** Set an IP address given by the four byte-parts */
+#define IP4_ADDR(ipaddr, a,b,c,d) \
+ (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \
+ ((u32_t)((b) & 0xff) << 16) | \
+ ((u32_t)((c) & 0xff) << 8) | \
+ (u32_t)((d) & 0xff)
+#else
+/** Set an IP address given by the four byte-parts.
+ Little-endian version that prevents the use of htonl. */
+#define IP4_ADDR(ipaddr, a,b,c,d) \
+ (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \
+ ((u32_t)((c) & 0xff) << 16) | \
+ ((u32_t)((b) & 0xff) << 8) | \
+ (u32_t)((a) & 0xff)
+#endif
+
+/** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR2_COPY
+#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip_addr_t))
+#endif
+
+/** Copy IP address - faster than ip_addr_set: no NULL check */
+#define ip_addr_copy(dest, src) ((dest).addr = (src).addr)
+/** Safely copy one IP address to another (src may be NULL) */
+#define ip_addr_set(dest, src) ((dest)->addr = \
+ ((src) == NULL ? 0 : \
+ (src)->addr))
+/** Set complete address to zero */
+#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0)
+/** Set address to IPADDR_ANY (no need for htonl()) */
+#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY)
+/** Set address to loopback address */
+#define ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK))
+/** Safely copy one IP address to another and change byte order
+ * from host- to network-order. */
+#define ip_addr_set_hton(dest, src) ((dest)->addr = \
+ ((src) == NULL ? 0:\
+ htonl((src)->addr)))
+/** IPv4 only: set the IP address given as an u32_t */
+#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32))
+/** IPv4 only: get the IP address as an u32_t */
+#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr)
+
+/** Get the network address by combining host address with netmask */
+#define ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr))
+
+/**
+ * Determine if two address are on the same network.
+ *
+ * @arg addr1 IP address 1
+ * @arg addr2 IP address 2
+ * @arg mask network identifier mask
+ * @return !0 if the network identifiers of both address match
+ */
+#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \
+ (mask)->addr) == \
+ ((addr2)->addr & \
+ (mask)->addr))
+#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr)
+
+#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY)
+
+#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif))
+u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif);
+
+#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr)
+u8_t ip4_addr_netmask_valid(u32_t netmask);
+
+#define ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL))
+
+#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL))
+
+#define ip_addr_debug_print(debug, ipaddr) \
+ LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \
+ ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \
+ ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \
+ ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \
+ ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0))
+
+/* Get one byte from the 4-byte address */
+#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
+#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
+#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
+#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3])
+/* These are cast to u16_t, with the intent that they are often arguments
+ * to printf using the U16_F format from cc.h. */
+#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
+#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
+#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
+#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
+
+/** For backwards compatibility */
+#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr)
+
+u32_t ipaddr_addr(const char *cp);
+int ipaddr_aton(const char *cp, ip_addr_t *addr);
+/** returns ptr to static buffer; not reentrant! */
+char *ipaddr_ntoa(const ip_addr_t *addr);
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_ADDR_H__ */
diff --git a/core/lwip/src/include/ipv4/lwip/ip_frag.h b/core/lwip/src/include/ipv4/lwip/ip_frag.h
new file mode 100644
index 00000000..77b5eb1e
--- /dev/null
+++ b/core/lwip/src/include/ipv4/lwip/ip_frag.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *
+ */
+
+#ifndef __LWIP_IP_FRAG_H__
+#define __LWIP_IP_FRAG_H__
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if IP_REASSEMBLY
+/* The IP reassembly timer interval in milliseconds. */
+#define IP_TMR_INTERVAL 1000
+
+/* IP reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip_reassdata {
+ struct ip_reassdata *next;
+ struct pbuf *p;
+ struct ip_hdr iphdr;
+ u16_t datagram_len;
+ u8_t flags;
+ u8_t timer;
+};
+
+void ip_reass_init(void);
+void ip_reass_tmr(void);
+struct pbuf * ip_reass(struct pbuf *p);
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+struct pbuf_custom_ref {
+ /** 'base class' */
+ struct pbuf_custom pc;
+ /** pointer to the original pbuf that is referenced */
+ struct pbuf *original;
+};
+#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+
+err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest);
+#endif /* IP_FRAG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_FRAG_H__ */
diff --git a/core/lwip/src/include/lwip/api.h b/core/lwip/src/include/lwip/api.h
new file mode 100644
index 00000000..91b9e5d2
--- /dev/null
+++ b/core/lwip/src/include/lwip/api.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_API_H__
+#define __LWIP_API_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/netbuf.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+
+/* Flags for netconn_write (u8_t) */
+#define NETCONN_NOFLAG 0x00
+#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */
+#define NETCONN_COPY 0x01
+#define NETCONN_MORE 0x02
+#define NETCONN_DONTBLOCK 0x04
+
+/* Flags for struct netconn.flags (u8_t) */
+/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores whether to wake up the original application task
+ if data couldn't be sent in the first try. */
+#define NETCONN_FLAG_WRITE_DELAYED 0x01
+/** Should this netconn avoid blocking? */
+#define NETCONN_FLAG_NON_BLOCKING 0x02
+/** Was the last connect action a non-blocking one? */
+#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04
+/** If this is set, a TCP netconn must call netconn_recved() to update
+ the TCP receive window (done automatically if not set). */
+#define NETCONN_FLAG_NO_AUTO_RECVED 0x08
+/** If a nonblocking write has been rejected before, poll_tcp needs to
+ check if the netconn is writable again */
+#define NETCONN_FLAG_CHECK_WRITESPACE 0x10
+
+
+/* Helpers to process several netconn_types by the same code */
+#define NETCONNTYPE_GROUP(t) (t&0xF0)
+#define NETCONNTYPE_DATAGRAM(t) (t&0xE0)
+
+/** Protocol family and type of the netconn */
+enum netconn_type {
+ NETCONN_INVALID = 0,
+ /* NETCONN_TCP Group */
+ NETCONN_TCP = 0x10,
+ /* NETCONN_UDP Group */
+ NETCONN_UDP = 0x20,
+ NETCONN_UDPLITE = 0x21,
+ NETCONN_UDPNOCHKSUM= 0x22,
+ /* NETCONN_RAW Group */
+ NETCONN_RAW = 0x40
+};
+
+/** Current state of the netconn. Non-TCP netconns are always
+ * in state NETCONN_NONE! */
+enum netconn_state {
+ NETCONN_NONE,
+ NETCONN_WRITE,
+ NETCONN_LISTEN,
+ NETCONN_CONNECT,
+ NETCONN_CLOSE
+};
+
+/** Use to inform the callback function about changes */
+enum netconn_evt {
+ NETCONN_EVT_RCVPLUS,
+ NETCONN_EVT_RCVMINUS,
+ NETCONN_EVT_SENDPLUS,
+ NETCONN_EVT_SENDMINUS,
+ NETCONN_EVT_ERROR
+};
+
+#if LWIP_IGMP
+/** Used for netconn_join_leave_group() */
+enum netconn_igmp {
+ NETCONN_JOIN,
+ NETCONN_LEAVE
+};
+#endif /* LWIP_IGMP */
+
+/* forward-declare some structs to avoid to include their headers */
+struct ip_pcb;
+struct tcp_pcb;
+struct udp_pcb;
+struct raw_pcb;
+struct netconn;
+struct api_msg_msg;
+
+/** A callback prototype to inform about events for a netconn */
+typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
+
+/** A netconn descriptor */
+struct netconn {
+ /** type of the netconn (TCP, UDP or RAW) */
+ enum netconn_type type;
+ /** current state of the netconn */
+ enum netconn_state state;
+ /** the lwIP internal protocol control block */
+ union {
+ struct ip_pcb *ip;
+ struct tcp_pcb *tcp;
+ struct udp_pcb *udp;
+ struct raw_pcb *raw;
+ } pcb;
+ /** the last error this netconn had */
+ err_t last_err;
+ /** sem that is used to synchroneously execute functions in the core context */
+ sys_sem_t op_completed;
+ /** mbox where received packets are stored until they are fetched
+ by the netconn application thread (can grow quite big) */
+ sys_mbox_t recvmbox;
+#if LWIP_TCP
+ /** mbox where new connections are stored until processed
+ by the application thread */
+ sys_mbox_t acceptmbox;
+#endif /* LWIP_TCP */
+ /** only used for socket layer */
+#if LWIP_SOCKET
+ int socket;
+#endif /* LWIP_SOCKET */
+#if LWIP_SO_RCVTIMEO
+ /** timeout to wait for new data to be received
+ (or connections to arrive for listening netconns) */
+ int recv_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ /** maximum amount of bytes queued in recvmbox
+ not used for TCP: adjust TCP_WND instead! */
+ int recv_bufsize;
+ /** number of bytes currently in recvmbox to be received,
+ tested against recv_bufsize to limit bytes on recvmbox
+ for UDP and RAW, used for FIONREAD */
+ s16_t recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+ /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
+ u8_t flags;
+#if LWIP_TCP
+ /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores how much is already sent. */
+ size_t write_offset;
+ /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores the message.
+ Also used during connect and close. */
+ struct api_msg_msg *current_msg;
+#endif /* LWIP_TCP */
+ /** A callback function that is informed about events for this netconn */
+ netconn_callback callback;
+};
+
+/** Register an Network connection event */
+#define API_EVENT(c,e,l) if (c->callback) { \
+ (*c->callback)(c, e, l); \
+ }
+
+/** Set conn->last_err to err but don't overwrite fatal errors */
+#define NETCONN_SET_SAFE_ERR(conn, err) do { \
+ SYS_ARCH_DECL_PROTECT(lev); \
+ SYS_ARCH_PROTECT(lev); \
+ if (!ERR_IS_FATAL((conn)->last_err)) { \
+ (conn)->last_err = err; \
+ } \
+ SYS_ARCH_UNPROTECT(lev); \
+} while(0);
+
+/* Network connection functions: */
+#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
+#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
+struct
+netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
+ netconn_callback callback);
+err_t netconn_delete(struct netconn *conn);
+/** Get the type of a netconn (as enum netconn_type). */
+#define netconn_type(conn) (conn->type)
+
+err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
+ u16_t *port, u8_t local);
+#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
+#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
+
+err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port);
+err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port);
+err_t netconn_disconnect (struct netconn *conn);
+err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
+#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
+err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
+err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
+err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
+void netconn_recved(struct netconn *conn, u32_t length);
+err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
+ ip_addr_t *addr, u16_t port);
+err_t netconn_send(struct netconn *conn, struct netbuf *buf);
+err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags);
+err_t netconn_close(struct netconn *conn);
+err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
+
+#if LWIP_IGMP
+err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr,
+ ip_addr_t *netif_addr, enum netconn_igmp join_or_leave);
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
+#endif /* LWIP_DNS */
+
+#define netconn_err(conn) ((conn)->last_err)
+#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize)
+
+/** Set the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_set_nonblocking(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0)
+/** Get the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
+
+/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
+#define netconn_set_noautorecved(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0)
+/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
+#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0)
+
+#if LWIP_SO_RCVTIMEO
+/** Set the receive timeout in milliseconds */
+#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout))
+/** Get the receive timeout in milliseconds */
+#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout)
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+/** Set the receive buffer in bytes */
+#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize))
+/** Get the receive buffer in bytes */
+#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize)
+#endif /* LWIP_SO_RCVBUF*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN */
+
+#endif /* __LWIP_API_H__ */
diff --git a/core/lwip/src/include/lwip/api_msg.h b/core/lwip/src/include/lwip/api_msg.h
new file mode 100644
index 00000000..f99d8c3b
--- /dev/null
+++ b/core/lwip/src/include/lwip/api_msg.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_API_MSG_H__
+#define __LWIP_API_MSG_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For the netconn API, these values are use as a bitmask! */
+#define NETCONN_SHUT_RD 1
+#define NETCONN_SHUT_WR 2
+#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR)
+
+/* IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+/** This struct includes everything that is necessary to execute a function
+ for a netconn in another thread context (mainly used to process netconns
+ in the tcpip_thread context to be thread safe). */
+struct api_msg_msg {
+ /** The netconn which to process - always needed: it includes the semaphore
+ which is used to block the application thread until the function finished. */
+ struct netconn *conn;
+ /** The return value of the function executed in tcpip_thread. */
+ err_t err;
+ /** Depending on the executed function, one of these union members is used */
+ union {
+ /** used for do_send */
+ struct netbuf *b;
+ /** used for do_newconn */
+ struct {
+ u8_t proto;
+ } n;
+ /** used for do_bind and do_connect */
+ struct {
+ ip_addr_t *ipaddr;
+ u16_t port;
+ } bc;
+ /** used for do_getaddr */
+ struct {
+ ip_addr_t *ipaddr;
+ u16_t *port;
+ u8_t local;
+ } ad;
+ /** used for do_write */
+ struct {
+ const void *dataptr;
+ size_t len;
+ u8_t apiflags;
+ } w;
+ /** used for do_recv */
+ struct {
+ u32_t len;
+ } r;
+ /** used for do_close (/shutdown) */
+ struct {
+ u8_t shut;
+ } sd;
+#if LWIP_IGMP
+ /** used for do_join_leave_group */
+ struct {
+ ip_addr_t *multiaddr;
+ ip_addr_t *netif_addr;
+ enum netconn_igmp join_or_leave;
+ } jl;
+#endif /* LWIP_IGMP */
+#if TCP_LISTEN_BACKLOG
+ struct {
+ u8_t backlog;
+ } lb;
+#endif /* TCP_LISTEN_BACKLOG */
+ } msg;
+};
+
+/** This struct contains a function to execute in another thread context and
+ a struct api_msg_msg that serves as an argument for this function.
+ This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */
+struct api_msg {
+ /** function to execute in tcpip_thread context */
+ void (* function)(struct api_msg_msg *msg);
+ /** arguments for this function */
+ struct api_msg_msg msg;
+};
+
+#if LWIP_DNS
+/** As do_gethostbyname requires more arguments but doesn't require a netconn,
+ it has its own struct (to avoid struct api_msg getting bigger than necessary).
+ do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
+ (see netconn_gethostbyname). */
+struct dns_api_msg {
+ /** Hostname to query or dotted IP address string */
+ const char *name;
+ /** Rhe resolved address is stored here */
+ ip_addr_t *addr;
+ /** This semaphore is posted when the name is resolved, the application thread
+ should wait on it. */
+ sys_sem_t *sem;
+ /** Errors are given back here */
+ err_t *err;
+};
+#endif /* LWIP_DNS */
+
+void do_newconn ( struct api_msg_msg *msg);
+void do_delconn ( struct api_msg_msg *msg);
+void do_bind ( struct api_msg_msg *msg);
+void do_connect ( struct api_msg_msg *msg);
+void do_disconnect ( struct api_msg_msg *msg);
+void do_listen ( struct api_msg_msg *msg);
+void do_send ( struct api_msg_msg *msg);
+void do_recv ( struct api_msg_msg *msg);
+void do_write ( struct api_msg_msg *msg);
+void do_getaddr ( struct api_msg_msg *msg);
+void do_close ( struct api_msg_msg *msg);
+void do_shutdown ( struct api_msg_msg *msg);
+#if LWIP_IGMP
+void do_join_leave_group( struct api_msg_msg *msg);
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+void do_gethostbyname(void *arg);
+#endif /* LWIP_DNS */
+
+struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
+void netconn_free(struct netconn *conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN */
+
+#endif /* __LWIP_API_MSG_H__ */
diff --git a/core/lwip/src/include/lwip/arch.h b/core/lwip/src/include/lwip/arch.h
new file mode 100644
index 00000000..524af6be
--- /dev/null
+++ b/core/lwip/src/include/lwip/arch.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ARCH_H__
+#define __LWIP_ARCH_H__
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#include "arch/cc.h"
+
+/** Temporary: define format string for size_t if not defined in cc.h */
+#ifndef SZT_F
+#define SZT_F U32_F
+#endif /* SZT_F */
+/** Temporary upgrade helper: define format string for u8_t as hex if not
+ defined in cc.h */
+#ifndef X8_F
+#define X8_F "02x"
+#endif /* X8_F */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef PACK_STRUCT_BEGIN
+#define PACK_STRUCT_BEGIN
+#endif /* PACK_STRUCT_BEGIN */
+
+#ifndef PACK_STRUCT_END
+#define PACK_STRUCT_END
+#endif /* PACK_STRUCT_END */
+
+#ifndef PACK_STRUCT_FIELD
+#define PACK_STRUCT_FIELD(x) x
+#endif /* PACK_STRUCT_FIELD */
+
+
+#ifndef LWIP_UNUSED_ARG
+#define LWIP_UNUSED_ARG(x) (void)x
+#endif /* LWIP_UNUSED_ARG */
+
+
+#ifdef LWIP_PROVIDE_ERRNO
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+#define ENOSYS 38 /* Function not implemented */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+
+#define EDEADLOCK EDEADLK
+
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale NFS file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+
+#define ENOMEDIUM 123 /* No medium found */
+#define EMEDIUMTYPE 124 /* Wrong medium type */
+
+
+#define ENSROK 0 /* DNS server returned answer with no data */
+#define ENSRNODATA 160 /* DNS server returned answer with no data */
+#define ENSRFORMERR 161 /* DNS server claims query was misformatted */
+#define ENSRSERVFAIL 162 /* DNS server returned general failure */
+#define ENSRNOTFOUND 163 /* Domain name not found */
+#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */
+#define ENSRREFUSED 165 /* DNS server refused query */
+#define ENSRBADQUERY 166 /* Misformatted DNS query */
+#define ENSRBADNAME 167 /* Misformatted domain name */
+#define ENSRBADFAMILY 168 /* Unsupported address family */
+#define ENSRBADRESP 169 /* Misformatted DNS reply */
+#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */
+#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */
+#define ENSROF 172 /* End of file */
+#define ENSRFILE 173 /* Error reading file */
+#define ENSRNOMEM 174 /* Out of memory */
+#define ENSRDESTRUCTION 175 /* Application terminated lookup */
+#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */
+#define ENSRCNAMELOOP 177 /* Domain name is too long */
+
+#ifndef errno
+extern int errno;
+#endif
+
+#endif /* LWIP_PROVIDE_ERRNO */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_ARCH_H__ */
diff --git a/core/lwip/src/include/lwip/debug.h b/core/lwip/src/include/lwip/debug.h
new file mode 100644
index 00000000..d8359ea3
--- /dev/null
+++ b/core/lwip/src/include/lwip/debug.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_DEBUG_H__
+#define __LWIP_DEBUG_H__
+
+#include "lwip/arch.h"
+
+/** lower two bits indicate debug level
+ * - 0 all
+ * - 1 warning
+ * - 2 serious
+ * - 3 severe
+ */
+#define LWIP_DBG_LEVEL_ALL 0x00
+#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */
+#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SEVERE 0x03
+#define LWIP_DBG_MASK_LEVEL 0x03
+
+/** flag for LWIP_DEBUGF to enable that debug message */
+#define LWIP_DBG_ON 0x80U
+/** flag for LWIP_DEBUGF to disable that debug message */
+#define LWIP_DBG_OFF 0x00U
+
+/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
+#define LWIP_DBG_TRACE 0x40U
+/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
+#define LWIP_DBG_STATE 0x20U
+/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */
+#define LWIP_DBG_FRESH 0x10U
+/** flag for LWIP_DEBUGF to halt after printing this debug message */
+#define LWIP_DBG_HALT 0x08U
+
+#ifndef LWIP_NOASSERT
+#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \
+ LWIP_PLATFORM_ASSERT(message); } while(0)
+#else /* LWIP_NOASSERT */
+#define LWIP_ASSERT(message, assertion)
+#endif /* LWIP_NOASSERT */
+
+/** if "expression" isn't true, then print "message" and execute "handler" expression */
+#ifndef LWIP_ERROR
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+ LWIP_PLATFORM_ASSERT(message); handler;}} while(0)
+#endif /* LWIP_ERROR */
+
+#ifdef LWIP_DEBUG
+/** print debug message only if debug message type is enabled...
+ * AND is of correct type AND is at least LWIP_DBG_LEVEL
+ */
+#define LWIP_DEBUGF(debug, message) do { \
+ if ( \
+ ((debug) & LWIP_DBG_ON) && \
+ ((debug) & LWIP_DBG_TYPES_ON) && \
+ ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
+ LWIP_PLATFORM_DIAG(message); \
+ if ((debug) & LWIP_DBG_HALT) { \
+ while(1); \
+ } \
+ } \
+ } while(0)
+
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUGF(debug, message)
+#endif /* LWIP_DEBUG */
+
+#endif /* __LWIP_DEBUG_H__ */
+
diff --git a/core/lwip/src/include/lwip/def.h b/core/lwip/src/include/lwip/def.h
new file mode 100644
index 00000000..9b6de6a8
--- /dev/null
+++ b/core/lwip/src/include/lwip/def.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_DEF_H__
+#define __LWIP_DEF_H__
+
+/* arch.h might define NULL already */
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y))
+#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y))
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+/** Get the absolute difference between 2 u32_t values (correcting overflows)
+ * 'a' is expected to be 'higher' (without overflow) than 'b'. */
+#define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1)))
+
+/* Endianess-optimized shifting of two u8_t to create one u16_t */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define LWIP_MAKE_U16(a, b) ((a << 8) | b)
+#else
+#define LWIP_MAKE_U16(a, b) ((b << 8) | a)
+#endif
+
+#ifndef LWIP_PLATFORM_BYTESWAP
+#define LWIP_PLATFORM_BYTESWAP 0
+#endif
+
+#ifndef LWIP_PREFIX_BYTEORDER_FUNCS
+/* workaround for naming collisions on some platforms */
+
+#ifdef htons
+#undef htons
+#endif /* htons */
+#ifdef htonl
+#undef htonl
+#endif /* htonl */
+#ifdef ntohs
+#undef ntohs
+#endif /* ntohs */
+#ifdef ntohl
+#undef ntohl
+#endif /* ntohl */
+
+#define htons(x) lwip_htons(x)
+#define ntohs(x) lwip_ntohs(x)
+#define htonl(x) lwip_htonl(x)
+#define ntohl(x) lwip_ntohl(x)
+#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define lwip_htons(x) (x)
+#define lwip_ntohs(x) (x)
+#define lwip_htonl(x) (x)
+#define lwip_ntohl(x) (x)
+#define PP_HTONS(x) (x)
+#define PP_NTOHS(x) (x)
+#define PP_HTONL(x) (x)
+#define PP_NTOHL(x) (x)
+#else /* BYTE_ORDER != BIG_ENDIAN */
+#if LWIP_PLATFORM_BYTESWAP
+#define lwip_htons(x) LWIP_PLATFORM_HTONS(x)
+#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x)
+#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x)
+#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x)
+#else /* LWIP_PLATFORM_BYTESWAP */
+u16_t lwip_htons(u16_t x);
+u16_t lwip_ntohs(u16_t x);
+u32_t lwip_htonl(u32_t x);
+u32_t lwip_ntohl(u32_t x);
+#endif /* LWIP_PLATFORM_BYTESWAP */
+
+/* These macros should be calculated by the preprocessor and are used
+ with compile-time constants only (so that there is no little-endian
+ overhead at runtime). */
+#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
+#define PP_NTOHS(x) PP_HTONS(x)
+#define PP_HTONL(x) ((((x) & 0xff) << 24) | \
+ (((x) & 0xff00) << 8) | \
+ (((x) & 0xff0000UL) >> 8) | \
+ (((x) & 0xff000000UL) >> 24))
+#define PP_NTOHL(x) PP_HTONL(x)
+
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_DEF_H__ */
+
diff --git a/core/lwip/src/include/lwip/dhcp.h b/core/lwip/src/include/lwip/dhcp.h
new file mode 100644
index 00000000..32d93381
--- /dev/null
+++ b/core/lwip/src/include/lwip/dhcp.h
@@ -0,0 +1,242 @@
+/** @file
+ */
+
+#ifndef __LWIP_DHCP_H__
+#define __LWIP_DHCP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** period (in seconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_SECS 60
+/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL)
+/** period (in milliseconds) of the application calling dhcp_fine_tmr() */
+#define DHCP_FINE_TIMER_MSECS 500
+
+#define DHCP_CHADDR_LEN 16U
+#define DHCP_SNAME_LEN 64U
+#define DHCP_FILE_LEN 128U
+
+struct dhcp
+{
+ /** transaction identifier of last sent request */
+ u32_t xid;
+ /** our connection to the DHCP server */
+ struct udp_pcb *pcb;
+ /** incoming msg */
+ struct dhcp_msg *msg_in;
+ /** current DHCP state machine state */
+ u8_t state;
+ /** retries of current request */
+ u8_t tries;
+#if LWIP_DHCP_AUTOIP_COOP
+ u8_t autoip_coop_state;
+#endif
+ u8_t subnet_mask_given;
+
+ struct pbuf *p_out; /* pbuf of outcoming msg */
+ struct dhcp_msg *msg_out; /* outgoing msg */
+ u16_t options_out_len; /* outgoing msg options length */
+ u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
+ u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
+ u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
+ ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */
+ ip_addr_t offered_ip_addr;
+ ip_addr_t offered_sn_mask;
+ ip_addr_t offered_gw_addr;
+
+ u32_t offered_t0_lease; /* lease period (in seconds) */
+ u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */
+ u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */
+ /* @todo: LWIP_DHCP_BOOTP_FILE configuration option?
+ integrate with possible TFTP-client for booting? */
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_t offered_si_addr;
+ char boot_file_name[DHCP_FILE_LEN];
+#endif /* LWIP_DHCP_BOOTPFILE */
+};
+
+/* MUST be compiled with "pack structs" or equivalent! */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** minimum set of fields of any DHCP message */
+struct dhcp_msg
+{
+ PACK_STRUCT_FIELD(u8_t op);
+ PACK_STRUCT_FIELD(u8_t htype);
+ PACK_STRUCT_FIELD(u8_t hlen);
+ PACK_STRUCT_FIELD(u8_t hops);
+ PACK_STRUCT_FIELD(u32_t xid);
+ PACK_STRUCT_FIELD(u16_t secs);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FIELD(ip_addr_p_t ciaddr);
+ PACK_STRUCT_FIELD(ip_addr_p_t yiaddr);
+ PACK_STRUCT_FIELD(ip_addr_p_t siaddr);
+ PACK_STRUCT_FIELD(ip_addr_p_t giaddr);
+ PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]);
+ PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]);
+ PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]);
+ PACK_STRUCT_FIELD(u32_t cookie);
+#define DHCP_MIN_OPTIONS_LEN 68U
+/** make sure user does not configure this too small */
+#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN))
+# undef DHCP_OPTIONS_LEN
+#endif
+/** allow this to be configured in lwipopts.h, but not too small */
+#if (!defined(DHCP_OPTIONS_LEN))
+/** set this to be sufficient for your options in outgoing DHCP msgs */
+# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN
+#endif
+ PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp);
+/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */
+#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0)
+void dhcp_cleanup(struct netif *netif);
+/** start DHCP configuration */
+err_t dhcp_start(struct netif *netif);
+/** enforce early lease renewal (not needed normally)*/
+err_t dhcp_renew(struct netif *netif);
+/** release the DHCP lease, usually called before dhcp_stop()*/
+err_t dhcp_release(struct netif *netif);
+/** stop DHCP configuration */
+void dhcp_stop(struct netif *netif);
+/** inform server of our manual IP address */
+void dhcp_inform(struct netif *netif);
+/** Handle a possible change in the network configuration */
+void dhcp_network_changed(struct netif *netif);
+
+/** if enabled, check whether the offered IP address is not in use, using ARP */
+#if DHCP_DOES_ARP_CHECK
+void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr);
+#endif
+
+/** to be called every minute */
+void dhcp_coarse_tmr(void);
+/** to be called every half second */
+void dhcp_fine_tmr(void);
+
+/** DHCP message item offsets and length */
+#define DHCP_OP_OFS 0
+#define DHCP_HTYPE_OFS 1
+#define DHCP_HLEN_OFS 2
+#define DHCP_HOPS_OFS 3
+#define DHCP_XID_OFS 4
+#define DHCP_SECS_OFS 8
+#define DHCP_FLAGS_OFS 10
+#define DHCP_CIADDR_OFS 12
+#define DHCP_YIADDR_OFS 16
+#define DHCP_SIADDR_OFS 20
+#define DHCP_GIADDR_OFS 24
+#define DHCP_CHADDR_OFS 28
+#define DHCP_SNAME_OFS 44
+#define DHCP_FILE_OFS 108
+#define DHCP_MSG_LEN 236
+
+#define DHCP_COOKIE_OFS DHCP_MSG_LEN
+#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4)
+
+#define DHCP_CLIENT_PORT 68
+#define DHCP_SERVER_PORT 67
+
+/** DHCP client states */
+#define DHCP_OFF 0
+#define DHCP_REQUESTING 1
+#define DHCP_INIT 2
+#define DHCP_REBOOTING 3
+#define DHCP_REBINDING 4
+#define DHCP_RENEWING 5
+#define DHCP_SELECTING 6
+#define DHCP_INFORMING 7
+#define DHCP_CHECKING 8
+#define DHCP_PERMANENT 9
+#define DHCP_BOUND 10
+/** not yet implemented #define DHCP_RELEASING 11 */
+#define DHCP_BACKING_OFF 12
+
+/** AUTOIP cooperatation flags */
+#define DHCP_AUTOIP_COOP_STATE_OFF 0
+#define DHCP_AUTOIP_COOP_STATE_ON 1
+
+#define DHCP_BOOTREQUEST 1
+#define DHCP_BOOTREPLY 2
+
+/** DHCP message types */
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#define DHCP_RELEASE 7
+#define DHCP_INFORM 8
+
+/** DHCP hardware type, currently only ethernet is supported */
+#define DHCP_HTYPE_ETH 1
+
+#define DHCP_MAGIC_COOKIE 0x63825363UL
+
+/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */
+
+/** BootP options */
+#define DHCP_OPTION_PAD 0
+#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */
+#define DHCP_OPTION_ROUTER 3
+#define DHCP_OPTION_DNS_SERVER 6
+#define DHCP_OPTION_HOSTNAME 12
+#define DHCP_OPTION_IP_TTL 23
+#define DHCP_OPTION_MTU 26
+#define DHCP_OPTION_BROADCAST 28
+#define DHCP_OPTION_TCP_TTL 37
+#define DHCP_OPTION_END 255
+
+/** DHCP options */
+#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */
+#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */
+#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */
+
+#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */
+#define DHCP_OPTION_MESSAGE_TYPE_LEN 1
+
+#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */
+#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */
+
+#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */
+#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2
+
+#define DHCP_OPTION_T1 58 /* T1 renewal time */
+#define DHCP_OPTION_T2 59 /* T2 rebinding time */
+#define DHCP_OPTION_US 60
+#define DHCP_OPTION_CLIENT_ID 61
+#define DHCP_OPTION_TFTP_SERVERNAME 66
+#define DHCP_OPTION_BOOTFILE 67
+
+/** possible combinations of overloading the file and sname fields with options */
+#define DHCP_OVERLOAD_NONE 0
+#define DHCP_OVERLOAD_FILE 1
+#define DHCP_OVERLOAD_SNAME 2
+#define DHCP_OVERLOAD_SNAME_FILE 3
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DHCP */
+
+#endif /*__LWIP_DHCP_H__*/
diff --git a/core/lwip/src/include/lwip/dns.h b/core/lwip/src/include/lwip/dns.h
new file mode 100644
index 00000000..6c7d9b07
--- /dev/null
+++ b/core/lwip/src/include/lwip/dns.h
@@ -0,0 +1,124 @@
+/**
+ * lwip DNS resolver header file.
+
+ * Author: Jim Pettinato
+ * April 2007
+
+ * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LWIP_DNS_H__
+#define __LWIP_DNS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** DNS timer period */
+#define DNS_TMR_INTERVAL 1000
+
+/** DNS field TYPE used for "Resource Records" */
+#define DNS_RRTYPE_A 1 /* a host address */
+#define DNS_RRTYPE_NS 2 /* an authoritative name server */
+#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */
+#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */
+#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */
+#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */
+#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */
+#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */
+#define DNS_RRTYPE_WKS 11 /* a well known service description */
+#define DNS_RRTYPE_PTR 12 /* a domain name pointer */
+#define DNS_RRTYPE_HINFO 13 /* host information */
+#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */
+#define DNS_RRTYPE_MX 15 /* mail exchange */
+#define DNS_RRTYPE_TXT 16 /* text strings */
+
+/** DNS field CLASS used for "Resource Records" */
+#define DNS_RRCLASS_IN 1 /* the Internet */
+#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
+#define DNS_RRCLASS_CH 3 /* the CHAOS class */
+#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */
+#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */
+
+/* The size used for the next line is rather a hack, but it prevents including socket.h in all files
+ that include memp.h, and that would possibly break portability (since socket.h defines some types
+ and constants possibly already define by the OS).
+ Calculation rule:
+ sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */
+#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1)
+
+#if DNS_LOCAL_HOSTLIST
+/** struct used for local host-list */
+struct local_hostlist_entry {
+ /** static hostname */
+ const char *name;
+ /** static host address in network byteorder */
+ ip_addr_t addr;
+ struct local_hostlist_entry *next;
+};
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN
+#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH
+#endif
+#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1))
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/** Callback which is invoked when a hostname is found.
+ * A function of this type must be implemented by the application using the DNS resolver.
+ * @param name pointer to the name that was looked up.
+ * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname,
+ * or NULL if the name could not be found (or on any other error).
+ * @param callback_arg a user-specified callback argument passed to dns_gethostbyname
+*/
+typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg);
+
+void dns_init(void);
+void dns_tmr(void);
+void dns_setserver(u8_t numdns, ip_addr_t *dnsserver);
+ip_addr_t dns_getserver(u8_t numdns);
+err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr,
+ dns_found_callback found, void *callback_arg);
+
+#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+int dns_local_removehost(const char *hostname, const ip_addr_t *addr);
+err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr);
+#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS */
+
+#endif /* __LWIP_DNS_H__ */
diff --git a/core/lwip/src/include/lwip/err.h b/core/lwip/src/include/lwip/err.h
new file mode 100644
index 00000000..ac907729
--- /dev/null
+++ b/core/lwip/src/include/lwip/err.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ERR_H__
+#define __LWIP_ERR_H__
+
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Define LWIP_ERR_T in cc.h if you want to use
+ * a different type for your platform (must be signed). */
+#ifdef LWIP_ERR_T
+typedef LWIP_ERR_T err_t;
+#else /* LWIP_ERR_T */
+typedef s8_t err_t;
+#endif /* LWIP_ERR_T*/
+
+/* Definitions for error constants. */
+
+#define ERR_OK 0 /* No error, everything OK. */
+#define ERR_MEM -1 /* Out of memory error. */
+#define ERR_BUF -2 /* Buffer error. */
+#define ERR_TIMEOUT -3 /* Timeout. */
+#define ERR_RTE -4 /* Routing problem. */
+#define ERR_INPROGRESS -5 /* Operation in progress */
+#define ERR_VAL -6 /* Illegal value. */
+#define ERR_WOULDBLOCK -7 /* Operation would block. */
+#define ERR_USE -8 /* Address in use. */
+#define ERR_ISCONN -9 /* Already connected. */
+
+#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN)
+
+#define ERR_ABRT -10 /* Connection aborted. */
+#define ERR_RST -11 /* Connection reset. */
+#define ERR_CLSD -12 /* Connection closed. */
+#define ERR_CONN -13 /* Not connected. */
+
+#define ERR_ARG -14 /* Illegal argument. */
+
+#define ERR_IF -15 /* Low-level netif error */
+
+
+#ifdef LWIP_DEBUG
+extern const char *lwip_strerr(err_t err);
+#else
+#define lwip_strerr(x) ""
+#endif /* LWIP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_ERR_H__ */
diff --git a/core/lwip/src/include/lwip/init.h b/core/lwip/src/include/lwip/init.h
new file mode 100644
index 00000000..77dcdfc7
--- /dev/null
+++ b/core/lwip/src/include/lwip/init.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_INIT_H__
+#define __LWIP_INIT_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** X.x.x: Major version of the stack */
+#define LWIP_VERSION_MAJOR 1U
+/** x.X.x: Minor version of the stack */
+#define LWIP_VERSION_MINOR 4U
+/** x.x.X: Revision of the stack */
+#define LWIP_VERSION_REVISION 0U
+/** For release candidates, this is set to 1..254
+ * For official releases, this is set to 255 (LWIP_RC_RELEASE)
+ * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */
+#define LWIP_VERSION_RC 255U
+
+/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */
+#define LWIP_RC_RELEASE 255U
+/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */
+#define LWIP_RC_DEVELOPMENT 0U
+
+#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE)
+#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT)
+#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT))
+
+/** Provides the version of the stack */
+#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \
+ LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC)
+
+/* Modules initialization */
+void lwip_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_INIT_H__ */
diff --git a/core/lwip/src/include/lwip/mem.h b/core/lwip/src/include/lwip/mem.h
new file mode 100644
index 00000000..4dbf2f64
--- /dev/null
+++ b/core/lwip/src/include/lwip/mem.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_MEM_H__
+#define __LWIP_MEM_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MEM_LIBC_MALLOC
+
+#include <stddef.h> /* for size_t */
+
+typedef size_t mem_size_t;
+
+/* aliases for C library malloc() */
+#define lwip_mem_init()
+/* in case C library malloc() needs extra protection,
+ * allow these defines to be overridden.
+ */
+#ifndef mem_free
+#define mem_free free
+#endif
+#ifndef mem_malloc
+#define mem_malloc malloc
+#endif
+#ifndef mem_calloc
+#define mem_calloc calloc
+#endif
+/* Since there is no C library allocation function to shrink memory without
+ moving it, define this to nothing. */
+#ifndef mem_trim
+#define mem_trim(mem, size) (mem)
+#endif
+#else /* MEM_LIBC_MALLOC */
+
+/* MEM_SIZE would have to be aligned, but using 64000 here instead of
+ * 65535 leaves some room for alignment...
+ */
+#if MEM_SIZE > 64000L
+typedef u32_t mem_size_t;
+#define MEM_SIZE_F U32_F
+#else
+typedef u16_t mem_size_t;
+#define MEM_SIZE_F U16_F
+#endif /* MEM_SIZE > 64000 */
+
+#if MEM_USE_POOLS
+/** lwip_mem_init is not used when using pools instead of a heap */
+#define lwip_mem_init()
+/** mem_trim is not used when using pools instead of a heap:
+ we can't free part of a pool element and don't want to copy the rest */
+#define mem_trim(mem, size) (mem)
+#else /* MEM_USE_POOLS */
+/* lwIP alternative malloc */
+void lwip_mem_init(void);
+void *mem_trim(void *mem, mem_size_t size);
+#endif /* MEM_USE_POOLS */
+void *mem_malloc(mem_size_t size);
+void *mem_calloc(mem_size_t count, mem_size_t size);
+void mem_free(void *mem);
+#endif /* MEM_LIBC_MALLOC */
+
+/** Calculate memory size for an aligned buffer - returns the next highest
+ * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and
+ * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4).
+ */
+#ifndef LWIP_MEM_ALIGN_SIZE
+#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))
+#endif
+
+/** Calculate safe memory size for an aligned buffer when using an unaligned
+ * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the
+ * start (e.g. if buffer is u8_t[] and actual data will be u32_t*)
+ */
+#ifndef LWIP_MEM_ALIGN_BUFFER
+#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1))
+#endif
+
+/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT
+ * so that ADDR % MEM_ALIGNMENT == 0
+ */
+#ifndef LWIP_MEM_ALIGN
+#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_MEM_H__ */
diff --git a/core/lwip/src/include/lwip/memp.h b/core/lwip/src/include/lwip/memp.h
new file mode 100644
index 00000000..f0d07399
--- /dev/null
+++ b/core/lwip/src/include/lwip/memp.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __LWIP_MEMP_H__
+#define __LWIP_MEMP_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
+typedef enum {
+#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
+#include "lwip/memp_std.h"
+ MEMP_MAX
+} memp_t;
+
+#if MEM_USE_POOLS
+/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
+typedef enum {
+ /* Get the first (via:
+ MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/
+ MEMP_POOL_HELPER_FIRST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START 1
+#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
+#define LWIP_MALLOC_MEMPOOL_END
+#include "lwip/memp_std.h"
+ ) ,
+ /* Get the last (via:
+ MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */
+ MEMP_POOL_HELPER_LAST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
+#define LWIP_MALLOC_MEMPOOL_END 1
+#include "lwip/memp_std.h"
+ )
+} memp_pool_helper_t;
+
+/* The actual start and stop values are here (cast them over)
+ We use this helper type and these defines so we can avoid using const memp_t values */
+#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST)
+#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST)
+#endif /* MEM_USE_POOLS */
+
+#if MEMP_MEM_MALLOC || MEM_USE_POOLS
+extern const u16_t memp_sizes[MEMP_MAX];
+#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */
+
+#if MEMP_MEM_MALLOC
+
+#include "mem.h"
+
+#define memp_init()
+#define memp_malloc(type) mem_malloc(memp_sizes[type])
+#define memp_free(type, mem) mem_free(mem)
+
+#else /* MEMP_MEM_MALLOC */
+
+#if MEM_USE_POOLS
+/** This structure is used to save the pool one element came from. */
+struct memp_malloc_helper
+{
+ memp_t poolnr;
+};
+#endif /* MEM_USE_POOLS */
+
+void memp_init(void);
+
+#if MEMP_OVERFLOW_CHECK
+void *memp_malloc_fn(memp_t type, const char* file, const int line);
+#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__)
+#else
+void *memp_malloc(memp_t type);
+#endif
+void memp_free(memp_t type, void *mem);
+
+#endif /* MEMP_MEM_MALLOC */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_MEMP_H__ */
diff --git a/core/lwip/src/include/lwip/memp_std.h b/core/lwip/src/include/lwip/memp_std.h
new file mode 100644
index 00000000..6ce408fb
--- /dev/null
+++ b/core/lwip/src/include/lwip/memp_std.h
@@ -0,0 +1,122 @@
+/*
+ * SETUP: Make sure we define everything we will need.
+ *
+ * We have create three types of pools:
+ * 1) MEMPOOL - standard pools
+ * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c
+ * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct
+ *
+ * If the include'r doesn't require any special treatment of each of the types
+ * above, then will declare #2 & #3 to be just standard mempools.
+ */
+#ifndef LWIP_MALLOC_MEMPOOL
+/* This treats "malloc pools" just like any other pool.
+ The pools are a little bigger to provide 'size' as the amount of user data. */
+#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL_END
+#endif /* LWIP_MALLOC_MEMPOOL */
+
+#ifndef LWIP_PBUF_MEMPOOL
+/* This treats "pbuf pools" just like any other pool.
+ * Allocates buffers for a pbuf struct AND a payload size */
+#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc)
+#endif /* LWIP_PBUF_MEMPOOL */
+
+
+/*
+ * A list of internal pools used by LWIP.
+ *
+ * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ */
+#if LWIP_RAW
+LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB")
+#endif /* LWIP_RAW */
+
+#if LWIP_UDP
+LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB")
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
+LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
+LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
+#endif /* LWIP_TCP */
+
+#if IP_REASSEMBLY
+LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA")
+#endif /* IP_REASSEMBLY */
+#if IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
+#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+
+#if LWIP_NETCONN
+LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF")
+LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN")
+#endif /* LWIP_NETCONN */
+
+#if NO_SYS==0
+LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API")
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT")
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+#endif /* NO_SYS==0 */
+
+#if ARP_QUEUEING
+LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE")
+#endif /* ARP_QUEUEING */
+
+#if LWIP_IGMP
+LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP")
+#endif /* LWIP_IGMP */
+
+#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */
+LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT")
+#endif /* LWIP_TIMERS */
+
+#if LWIP_SNMP
+LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE")
+LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE")
+LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND")
+LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE")
+#endif /* LWIP_SNMP */
+#if LWIP_DNS && LWIP_SOCKET
+LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB")
+#endif /* LWIP_DNS && LWIP_SOCKET */
+#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST")
+#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#if PPP_SUPPORT && PPPOE_SUPPORT
+LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF")
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
+
+/*
+ * A list of pools of pbuf's used by LWIP.
+ *
+ * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ * This allocates enough space for the pbuf struct and a payload.
+ * (Example: pbuf_payload_size=0 allocates only size for the struct)
+ */
+LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM")
+LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL")
+
+
+/*
+ * Allow for user-defined pools; this must be explicitly set in lwipopts.h
+ * since the default is to NOT look for lwippools.h
+ */
+#if MEMP_USE_CUSTOM_POOLS
+#include "lwippools.h"
+#endif /* MEMP_USE_CUSTOM_POOLS */
+
+/*
+ * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later
+ * (#undef is ignored for something that is not defined)
+ */
+#undef LWIP_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL_START
+#undef LWIP_MALLOC_MEMPOOL_END
+#undef LWIP_PBUF_MEMPOOL
diff --git a/core/lwip/src/include/lwip/netbuf.h b/core/lwip/src/include/lwip/netbuf.h
new file mode 100644
index 00000000..7d247d71
--- /dev/null
+++ b/core/lwip/src/include/lwip/netbuf.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_NETBUF_H__
+#define __LWIP_NETBUF_H__
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This netbuf has dest-addr/port set */
+#define NETBUF_FLAG_DESTADDR 0x01
+/** This netbuf includes a checksum */
+#define NETBUF_FLAG_CHKSUM 0x02
+
+struct netbuf {
+ struct pbuf *p, *ptr;
+ ip_addr_t addr;
+ u16_t port;
+#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
+#if LWIP_CHECKSUM_ON_COPY
+ u8_t flags;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ u16_t toport_chksum;
+#if LWIP_NETBUF_RECVINFO
+ ip_addr_t toaddr;
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+};
+
+/* Network buffer functions: */
+struct netbuf * netbuf_new (void);
+void netbuf_delete (struct netbuf *buf);
+void * netbuf_alloc (struct netbuf *buf, u16_t size);
+void netbuf_free (struct netbuf *buf);
+err_t netbuf_ref (struct netbuf *buf,
+ const void *dataptr, u16_t size);
+void netbuf_chain (struct netbuf *head,
+ struct netbuf *tail);
+
+err_t netbuf_data (struct netbuf *buf,
+ void **dataptr, u16_t *len);
+s8_t netbuf_next (struct netbuf *buf);
+void netbuf_first (struct netbuf *buf);
+
+
+#define netbuf_copy_partial(buf, dataptr, len, offset) \
+ pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))
+#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0)
+#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len)
+#define netbuf_len(buf) ((buf)->p->tot_len)
+#define netbuf_fromaddr(buf) (&((buf)->addr))
+#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set((&(buf)->addr), fromaddr)
+#define netbuf_fromport(buf) ((buf)->port)
+#if LWIP_NETBUF_RECVINFO
+#define netbuf_destaddr(buf) (&((buf)->toaddr))
+#define netbuf_set_destaddr(buf, destaddr) ip_addr_set((&(buf)->addr), destaddr)
+#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0)
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_CHECKSUM_ON_COPY
+#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \
+ (buf)->toport_chksum = chksum; } while(0)
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_NETBUF_H__ */
diff --git a/core/lwip/src/include/lwip/netdb.h b/core/lwip/src/include/lwip/netdb.h
new file mode 100644
index 00000000..7587e2f2
--- /dev/null
+++ b/core/lwip/src/include/lwip/netdb.h
@@ -0,0 +1,124 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef __LWIP_NETDB_H__
+#define __LWIP_NETDB_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/inet.h"
+#include "lwip/sockets.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* some rarely used options */
+#ifndef LWIP_DNS_API_DECLARE_H_ERRNO
+#define LWIP_DNS_API_DECLARE_H_ERRNO 1
+#endif
+
+#ifndef LWIP_DNS_API_DEFINE_ERRORS
+#define LWIP_DNS_API_DEFINE_ERRORS 1
+#endif
+
+#ifndef LWIP_DNS_API_DECLARE_STRUCTS
+#define LWIP_DNS_API_DECLARE_STRUCTS 1
+#endif
+
+#if LWIP_DNS_API_DEFINE_ERRORS
+/** Errors used by the DNS API functions, h_errno can be one of them */
+#define EAI_NONAME 200
+#define EAI_SERVICE 201
+#define EAI_FAIL 202
+#define EAI_MEMORY 203
+
+#define HOST_NOT_FOUND 210
+#define NO_DATA 211
+#define NO_RECOVERY 212
+#define TRY_AGAIN 213
+#endif /* LWIP_DNS_API_DEFINE_ERRORS */
+
+#if LWIP_DNS_API_DECLARE_STRUCTS
+struct hostent {
+ char *h_name; /* Official name of the host. */
+ char **h_aliases; /* A pointer to an array of pointers to alternative host names,
+ terminated by a null pointer. */
+ int h_addrtype; /* Address type. */
+ int h_length; /* The length, in bytes, of the address. */
+ char **h_addr_list; /* A pointer to an array of pointers to network addresses (in
+ network byte order) for the host, terminated by a null pointer. */
+#define h_addr h_addr_list[0] /* for backward compatibility */
+};
+
+struct addrinfo {
+ int ai_flags; /* Input flags. */
+ int ai_family; /* Address family of socket. */
+ int ai_socktype; /* Socket type. */
+ int ai_protocol; /* Protocol of socket. */
+ socklen_t ai_addrlen; /* Length of socket address. */
+ struct sockaddr *ai_addr; /* Socket address of socket. */
+ char *ai_canonname; /* Canonical name of service location. */
+ struct addrinfo *ai_next; /* Pointer to next in list. */
+};
+#endif /* LWIP_DNS_API_DECLARE_STRUCTS */
+
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+/* application accessable error code set by the DNS API functions */
+extern int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/
+
+struct hostent *lwip_gethostbyname(const char *name);
+int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop);
+void lwip_freeaddrinfo(struct addrinfo *ai);
+int lwip_getaddrinfo(const char *nodename,
+ const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+
+#if LWIP_COMPAT_SOCKETS
+#define gethostbyname(name) lwip_gethostbyname(name)
+#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \
+ lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop)
+#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo)
+#define getaddrinfo(nodname, servname, hints, res) \
+ lwip_getaddrinfo(nodname, servname, hints, res)
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
+
+#endif /* __LWIP_NETDB_H__ */
diff --git a/core/lwip/src/include/lwip/netif.h b/core/lwip/src/include/lwip/netif.h
new file mode 100644
index 00000000..8e799295
--- /dev/null
+++ b/core/lwip/src/include/lwip/netif.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_NETIF_H__
+#define __LWIP_NETIF_H__
+
+#include "lwip/opt.h"
+
+#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF)
+
+#include "lwip/err.h"
+
+#include "lwip/ip_addr.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#if LWIP_DHCP
+struct dhcp;
+#endif
+#if LWIP_AUTOIP
+struct autoip;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses are expected to be in
+ * the same byte order as in IP_PCB. */
+
+/** must be the maximum of all used hardware address lengths
+ across all types of interfaces in use */
+#define NETIF_MAX_HWADDR_LEN 32U
+
+/** Whether the network interface is 'up'. This is
+ * a software flag used to control whether this network
+ * interface is enabled and processes traffic.
+ * It is set by the startup code (for static IP configuration) or
+ * by dhcp/autoip when an address has been assigned.
+ */
+#define NETIF_FLAG_UP 0x01U
+/** If set, the netif has broadcast capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_BROADCAST 0x02U
+/** If set, the netif is one end of a point-to-point connection.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_POINTTOPOINT 0x04U
+/** If set, the interface is configured using DHCP.
+ * Set by the DHCP code when starting or stopping DHCP. */
+#define NETIF_FLAG_DHCP 0x08U
+/** If set, the interface has an active link
+ * (set by the network interface driver).
+ * Either set by the netif driver in its init function (if the link
+ * is up at that time) or at a later point once the link comes up
+ * (if link detection is supported by the hardware). */
+#define NETIF_FLAG_LINK_UP 0x10U
+/** If set, the netif is an ethernet device using ARP.
+ * Set by the netif driver in its init function.
+ * Used to check input packet types and use of DHCP. */
+#define NETIF_FLAG_ETHARP 0x20U
+/** If set, the netif is an ethernet device. It might not use
+ * ARP or TCP/IP if it is used for PPPoE only.
+ */
+#define NETIF_FLAG_ETHERNET 0x40U
+/** If set, the netif has IGMP capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_IGMP 0x80U
+
+/** Function prototype for netif init functions. Set up flags and output/linkoutput
+ * callback functions in this function.
+ *
+ * @param netif The netif to initialize
+ */
+typedef err_t (*netif_init_fn)(struct netif *netif);
+/** Function prototype for netif->input functions. This function is saved as 'input'
+ * callback function in the netif struct. Call it when a packet has been received.
+ *
+ * @param p The received packet, copied into a pbuf
+ * @param inp The netif which received the packet
+ */
+typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);
+/** Function prototype for netif->output functions. Called by lwIP when a packet
+ * shall be sent. For ethernet netif, set this to 'etharp_output' and set
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IP address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
+ ip_addr_t *ipaddr);
+/** Function prototype for netif->linkoutput functions. Only used for ethernet
+ * netifs. This function is called by ARP when a packet shall be sent.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (raw ethernet packet)
+ */
+typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p);
+/** Function prototype for netif status- or link-callback functions. */
+typedef void (*netif_status_callback_fn)(struct netif *netif);
+/** Function prototype for netif igmp_mac_filter functions */
+typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif,
+ ip_addr_t *group, u8_t action);
+
+/** Generic data structure used for all lwIP network interfaces.
+ * The following fields should be filled in by the initialization
+ * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
+struct netif {
+ /** pointer to next in linked list */
+ struct netif *next;
+
+ /** IP address configuration in network byte order */
+ ip_addr_t ip_addr;
+ ip_addr_t netmask;
+ ip_addr_t gw;
+
+ /** This function is called by the network device driver
+ * to pass a packet up the TCP/IP stack. */
+ netif_input_fn input;
+ /** This function is called by the IP module when it wants
+ * to send a packet on the interface. This function typically
+ * first resolves the hardware address, then sends the packet. */
+ netif_output_fn output;
+ /** This function is called by the ARP module when it wants
+ * to send a packet on the interface. This function outputs
+ * the pbuf as-is on the link medium. */
+ netif_linkoutput_fn linkoutput;
+#if LWIP_NETIF_STATUS_CALLBACK
+ /** This function is called when the netif state is set to up or down
+ */
+ netif_status_callback_fn status_callback;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ /** This function is called when the netif link is set to up or down
+ */
+ netif_status_callback_fn link_callback;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+ /** This field can be set by the device driver and could point
+ * to state information for the device. */
+ void *state;
+#if LWIP_DHCP
+ /** the DHCP client state information for this netif */
+ struct dhcp *dhcp;
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ /** the AutoIP client state information for this netif */
+ struct autoip *autoip;
+#endif
+#if LWIP_NETIF_HOSTNAME
+ /* the hostname for this netif, NULL is a valid value */
+ char* hostname;
+#endif /* LWIP_NETIF_HOSTNAME */
+ /** maximum transfer unit (in bytes) */
+ u16_t mtu;
+ /** number of bytes used in hwaddr */
+ u8_t hwaddr_len;
+ /** link level hardware address of this interface */
+ u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
+ /** flags (see NETIF_FLAG_ above) */
+ u8_t flags;
+ /** descriptive abbreviation */
+ char name[2];
+ /** number of this interface */
+ u8_t num;
+#if LWIP_SNMP
+ /** link type (from "snmp_ifType" enum from snmp.h) */
+ u8_t link_type;
+ /** (estimate) link speed */
+ u32_t link_speed;
+ /** timestamp at last change made (up/down) */
+ u32_t ts;
+ /** counters */
+ u32_t ifinoctets;
+ u32_t ifinucastpkts;
+ u32_t ifinnucastpkts;
+ u32_t ifindiscards;
+ u32_t ifoutoctets;
+ u32_t ifoutucastpkts;
+ u32_t ifoutnucastpkts;
+ u32_t ifoutdiscards;
+#endif /* LWIP_SNMP */
+#if LWIP_IGMP
+ /** This function could be called to add or delete a entry in the multicast
+ filter table of the ethernet MAC.*/
+ netif_igmp_mac_filter_fn igmp_mac_filter;
+#endif /* LWIP_IGMP */
+#if LWIP_NETIF_HWADDRHINT
+ u8_t *addr_hint;
+#endif /* LWIP_NETIF_HWADDRHINT */
+#if ENABLE_LOOPBACK
+ /* List of packets to be queued for ourselves. */
+ struct pbuf *loop_first;
+ struct pbuf *loop_last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u16_t loop_cnt_current;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#endif /* ENABLE_LOOPBACK */
+};
+
+#if LWIP_SNMP
+#define NETIF_INIT_SNMP(netif, type, speed) \
+ /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \
+ (netif)->link_type = (type); \
+ /* your link speed here (units: bits per second) */ \
+ (netif)->link_speed = (speed); \
+ (netif)->ts = 0; \
+ (netif)->ifinoctets = 0; \
+ (netif)->ifinucastpkts = 0; \
+ (netif)->ifinnucastpkts = 0; \
+ (netif)->ifindiscards = 0; \
+ (netif)->ifoutoctets = 0; \
+ (netif)->ifoutucastpkts = 0; \
+ (netif)->ifoutnucastpkts = 0; \
+ (netif)->ifoutdiscards = 0
+#else /* LWIP_SNMP */
+#define NETIF_INIT_SNMP(netif, type, speed)
+#endif /* LWIP_SNMP */
+
+
+/** The list of network interfaces. */
+extern struct netif *netif_list;
+/** The default network interface. */
+extern struct netif *netif_default;
+
+void netif_init(void);
+
+struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input);
+
+void
+netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw);
+void netif_remove(struct netif * netif);
+
+/* Returns a network interface given its name. The name is of the form
+ "et0", where the first two letters are the "name" field in the
+ netif structure, and the digit is in the num field in the same
+ structure. */
+struct netif *netif_find(char *name);
+
+void netif_set_default(struct netif *netif);
+
+void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr);
+void netif_set_netmask(struct netif *netif, ip_addr_t *netmask);
+void netif_set_gw(struct netif *netif, ip_addr_t *gw);
+
+void netif_set_up(struct netif *netif);
+void netif_set_down(struct netif *netif);
+/** Ask if an interface is up */
+#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_STATUS_CALLBACK
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback);
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+void netif_set_link_up(struct netif *netif);
+void netif_set_link_down(struct netif *netif);
+/** Ask if a link is up */
+#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_LINK_CALLBACK
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback);
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if LWIP_NETIF_HOSTNAME
+#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0)
+#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL)
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if LWIP_IGMP
+#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0)
+#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL)
+#endif /* LWIP_IGMP */
+
+#if ENABLE_LOOPBACK
+err_t netif_loop_output(struct netif *netif, struct pbuf *p, ip_addr_t *dest_ip);
+void netif_poll(struct netif *netif);
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+void netif_poll_all(void);
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_NETIF_H__ */
diff --git a/core/lwip/src/include/lwip/netifapi.h b/core/lwip/src/include/lwip/netifapi.h
new file mode 100644
index 00000000..33318efa
--- /dev/null
+++ b/core/lwip/src/include/lwip/netifapi.h
@@ -0,0 +1,108 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef __LWIP_NETIFAPI_H__
+#define __LWIP_NETIFAPI_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*netifapi_void_fn)(struct netif *netif);
+typedef err_t (*netifapi_errt_fn)(struct netif *netif);
+
+struct netifapi_msg_msg {
+#if !LWIP_TCPIP_CORE_LOCKING
+ sys_sem_t sem;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+ err_t err;
+ struct netif *netif;
+ union {
+ struct {
+ ip_addr_t *ipaddr;
+ ip_addr_t *netmask;
+ ip_addr_t *gw;
+ void *state;
+ netif_init_fn init;
+ netif_input_fn input;
+ } add;
+ struct {
+ netifapi_void_fn voidfunc;
+ netifapi_errt_fn errtfunc;
+ } common;
+ } msg;
+};
+
+struct netifapi_msg {
+ void (* function)(struct netifapi_msg_msg *msg);
+ struct netifapi_msg_msg msg;
+};
+
+
+/* API for application */
+err_t netifapi_netif_add ( struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input);
+
+err_t netifapi_netif_set_addr ( struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw );
+
+err_t netifapi_netif_common ( struct netif *netif,
+ netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc);
+
+#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
+#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
+#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
+#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start)
+#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL)
+#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start)
+#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETIF_API */
+
+#endif /* __LWIP_NETIFAPI_H__ */
diff --git a/core/lwip/src/include/lwip/opt.h b/core/lwip/src/include/lwip/opt.h
new file mode 100644
index 00000000..490aab43
--- /dev/null
+++ b/core/lwip/src/include/lwip/opt.h
@@ -0,0 +1,2071 @@
+/**
+ * @file
+ *
+ * lwIP Options Configuration
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_OPT_H__
+#define __LWIP_OPT_H__
+
+/*
+ * Include user defined options first. Anything not defined in these files
+ * will be set to standard values. Override anything you dont like!
+ */
+#include "lwipopts.h"
+#include "lwip/debug.h"
+
+/*
+ -----------------------------------------------
+ ---------- Platform specific locking ----------
+ -----------------------------------------------
+*/
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
+ * critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#ifndef SYS_LIGHTWEIGHT_PROT
+#define SYS_LIGHTWEIGHT_PROT 0
+#endif
+
+/**
+ * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
+ * use lwIP facilities.
+ */
+#ifndef NO_SYS
+#define NO_SYS 0
+#endif
+
+/**
+ * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
+ * Mainly for compatibility to old versions.
+ */
+#ifndef NO_SYS_NO_TIMERS
+#define NO_SYS_NO_TIMERS 0
+#endif
+
+/**
+ * MEMCPY: override this if you have a faster implementation at hand than the
+ * one included in your C library
+ */
+#ifndef MEMCPY
+#define MEMCPY(dst,src,len) memcpy(dst,src,len)
+#endif
+
+/**
+ * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a
+ * call to memcpy() if the length is known at compile time and is small.
+ */
+#ifndef SMEMCPY
+#define SMEMCPY(dst,src,len) memcpy(dst,src,len)
+#endif
+
+/*
+ ------------------------------------
+ ---------- Memory options ----------
+ ------------------------------------
+*/
+/**
+ * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library
+ * instead of the lwip internal allocator. Can save code size if you
+ * already use it.
+ */
+#ifndef MEM_LIBC_MALLOC
+#define MEM_LIBC_MALLOC 0
+#endif
+
+/**
+* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
+* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
+* speed and usage from interrupts!
+*/
+#ifndef MEMP_MEM_MALLOC
+#define MEMP_MEM_MALLOC 0
+#endif
+
+/**
+ * MEM_ALIGNMENT: should be set to the alignment of the CPU
+ * 4 byte alignment -> #define MEM_ALIGNMENT 4
+ * 2 byte alignment -> #define MEM_ALIGNMENT 2
+ */
+#ifndef MEM_ALIGNMENT
+#define MEM_ALIGNMENT 1
+#endif
+
+/**
+ * MEM_SIZE: the size of the heap memory. If the application will send
+ * a lot of data that needs to be copied, this should be set high.
+ */
+#ifndef MEM_SIZE
+#define MEM_SIZE 1600
+#endif
+
+/**
+ * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array.
+ * This can be used to individually change the location of each pool.
+ * Default is one big array for all pools
+ */
+#ifndef MEMP_SEPARATE_POOLS
+#define MEMP_SEPARATE_POOLS 0
+#endif
+
+/**
+ * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable
+ * amount of bytes before and after each memp element in every pool and fills
+ * it with a prominent default value.
+ * MEMP_OVERFLOW_CHECK == 0 no checking
+ * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed
+ * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time
+ * memp_malloc() or memp_free() is called (useful but slow!)
+ */
+#ifndef MEMP_OVERFLOW_CHECK
+#define MEMP_OVERFLOW_CHECK 0
+#endif
+
+/**
+ * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make
+ * sure that there are no cycles in the linked lists.
+ */
+#ifndef MEMP_SANITY_CHECK
+#define MEMP_SANITY_CHECK 0
+#endif
+
+/**
+ * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set
+ * of memory pools of various sizes. When mem_malloc is called, an element of
+ * the smallest pool that can provide the length needed is returned.
+ * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
+ */
+#ifndef MEM_USE_POOLS
+#define MEM_USE_POOLS 0
+#endif
+
+/**
+ * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next
+ * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more
+ * reliable. */
+#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL
+#define MEM_USE_POOLS_TRY_BIGGER_POOL 0
+#endif
+
+/**
+ * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
+ * that defines additional pools beyond the "standard" ones required
+ * by lwIP. If you set this to 1, you must have lwippools.h in your
+ * inlude path somewhere.
+ */
+#ifndef MEMP_USE_CUSTOM_POOLS
+#define MEMP_USE_CUSTOM_POOLS 0
+#endif
+
+/**
+ * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from
+ * interrupt context (or another context that doesn't allow waiting for a
+ * semaphore).
+ * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT,
+ * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs
+ * with each loop so that mem_free can run.
+ *
+ * ATTENTION: As you can see from the above description, this leads to dis-/
+ * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc
+ * can need longer.
+ *
+ * If you don't want that, at least for NO_SYS=0, you can still use the following
+ * functions to enqueue a deallocation call which then runs in the tcpip_thread
+ * context:
+ * - pbuf_free_callback(p);
+ * - mem_free_callback(m);
+ */
+#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0
+#endif
+
+/*
+ ------------------------------------------------
+ ---------- Internal Memory Pool Sizes ----------
+ ------------------------------------------------
+*/
+/**
+ * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF).
+ * If the application sends a lot of data out of ROM (or other static memory),
+ * this should be set high.
+ */
+#ifndef MEMP_NUM_PBUF
+#define MEMP_NUM_PBUF 16
+#endif
+
+/**
+ * MEMP_NUM_RAW_PCB: Number of raw connection PCBs
+ * (requires the LWIP_RAW option)
+ */
+#ifndef MEMP_NUM_RAW_PCB
+#define MEMP_NUM_RAW_PCB 4
+#endif
+
+/**
+ * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ * per active UDP "connection".
+ * (requires the LWIP_UDP option)
+ */
+#ifndef MEMP_NUM_UDP_PCB
+#define MEMP_NUM_UDP_PCB 4
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#ifndef MEMP_NUM_TCP_PCB
+#define MEMP_NUM_TCP_PCB 5
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#ifndef MEMP_NUM_TCP_PCB_LISTEN
+#define MEMP_NUM_TCP_PCB_LISTEN 8
+#endif
+
+/**
+ * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
+ * (requires the LWIP_TCP option)
+ */
+#ifndef MEMP_NUM_TCP_SEG
+#define MEMP_NUM_TCP_SEG 16
+#endif
+
+/**
+ * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for
+ * reassembly (whole packets, not fragments!)
+ */
+#ifndef MEMP_NUM_REASSDATA
+#define MEMP_NUM_REASSDATA 5
+#endif
+
+/**
+ * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent
+ * (fragments, not whole packets!).
+ * This is only used with IP_FRAG_USES_STATIC_BUF==0 and
+ * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs
+ * where the packet is not yet sent when netif->output returns.
+ */
+#ifndef MEMP_NUM_FRAG_PBUF
+#define MEMP_NUM_FRAG_PBUF 15
+#endif
+
+/**
+ * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing
+ * packets (pbufs) that are waiting for an ARP request (to resolve
+ * their destination address) to finish.
+ * (requires the ARP_QUEUEING option)
+ */
+#ifndef MEMP_NUM_ARP_QUEUE
+#define MEMP_NUM_ARP_QUEUE 30
+#endif
+
+/**
+ * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces
+ * can be members et the same time (one per netif - allsystems group -, plus one
+ * per netif membership).
+ * (requires the LWIP_IGMP option)
+ */
+#ifndef MEMP_NUM_IGMP_GROUP
+#define MEMP_NUM_IGMP_GROUP 8
+#endif
+
+/**
+ * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
+ * (requires NO_SYS==0)
+ */
+#ifndef MEMP_NUM_SYS_TIMEOUT
+#define MEMP_NUM_SYS_TIMEOUT 3
+#endif
+
+/**
+ * MEMP_NUM_NETBUF: the number of struct netbufs.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#ifndef MEMP_NUM_NETBUF
+#define MEMP_NUM_NETBUF 2
+#endif
+
+/**
+ * MEMP_NUM_NETCONN: the number of struct netconns.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#ifndef MEMP_NUM_NETCONN
+#define MEMP_NUM_NETCONN 4
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used
+ * for callback/timeout API communication.
+ * (only needed if you use tcpip.c)
+ */
+#ifndef MEMP_NUM_TCPIP_MSG_API
+#define MEMP_NUM_TCPIP_MSG_API 8
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used
+ * for incoming packets.
+ * (only needed if you use tcpip.c)
+ */
+#ifndef MEMP_NUM_TCPIP_MSG_INPKT
+#define MEMP_NUM_TCPIP_MSG_INPKT 8
+#endif
+
+/**
+ * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree.
+ */
+#ifndef MEMP_NUM_SNMP_NODE
+#define MEMP_NUM_SNMP_NODE 50
+#endif
+
+/**
+ * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree.
+ * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least!
+ */
+#ifndef MEMP_NUM_SNMP_ROOTNODE
+#define MEMP_NUM_SNMP_ROOTNODE 30
+#endif
+
+/**
+ * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to
+ * be changed normally) - 2 of these are used per request (1 for input,
+ * 1 for output)
+ */
+#ifndef MEMP_NUM_SNMP_VARBIND
+#define MEMP_NUM_SNMP_VARBIND 2
+#endif
+
+/**
+ * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used
+ * (does not have to be changed normally) - 3 of these are used per request
+ * (1 for the value read and 2 for OIDs - input and output)
+ */
+#ifndef MEMP_NUM_SNMP_VALUE
+#define MEMP_NUM_SNMP_VALUE 3
+#endif
+
+/**
+ * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls
+ * (before freeing the corresponding memory using lwip_freeaddrinfo()).
+ */
+#ifndef MEMP_NUM_NETDB
+#define MEMP_NUM_NETDB 1
+#endif
+
+/**
+ * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list
+ * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1.
+ */
+#ifndef MEMP_NUM_LOCALHOSTLIST
+#define MEMP_NUM_LOCALHOSTLIST 1
+#endif
+
+/**
+ * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE
+ * interfaces (only used with PPPOE_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOE_INTERFACES
+#define MEMP_NUM_PPPOE_INTERFACES 1
+#endif
+
+/**
+ * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ */
+#ifndef PBUF_POOL_SIZE
+#define PBUF_POOL_SIZE 16
+#endif
+
+/*
+ ---------------------------------
+ ---------- ARP options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_ARP==1: Enable ARP functionality.
+ */
+#ifndef LWIP_ARP
+#define LWIP_ARP 1
+#endif
+
+/**
+ * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached.
+ */
+#ifndef ARP_TABLE_SIZE
+#define ARP_TABLE_SIZE 10
+#endif
+
+/**
+ * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address
+ * resolution. By default, only the most recent packet is queued per IP address.
+ * This is sufficient for most protocols and mainly reduces TCP connection
+ * startup time. Set this to 1 if you know your application sends more than one
+ * packet in a row to an IP address that is not in the ARP cache.
+ */
+#ifndef ARP_QUEUEING
+#define ARP_QUEUEING 0
+#endif
+
+/**
+ * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be
+ * updated with the source MAC and IP addresses supplied in the packet.
+ * You may want to disable this if you do not trust LAN peers to have the
+ * correct addresses, or as a limited approach to attempt to handle
+ * spoofing. If disabled, lwIP will need to make a new ARP request if
+ * the peer is not already in the ARP table, adding a little latency.
+ * The peer *is* in the ARP table if it requested our address before.
+ * Also notice that this slows down input processing of every IP packet!
+ */
+#ifndef ETHARP_TRUST_IP_MAC
+#define ETHARP_TRUST_IP_MAC 0
+#endif
+
+/**
+ * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header.
+ * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check.
+ * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted.
+ * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted.
+ */
+#ifndef ETHARP_SUPPORT_VLAN
+#define ETHARP_SUPPORT_VLAN 0
+#endif
+
+/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP
+ * might be disabled
+ */
+#ifndef LWIP_ETHERNET
+#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT)
+#endif
+
+/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure
+ * alignment of payload after that header. Since the header is 14 bytes long,
+ * without this padding e.g. addresses in the IP header will not be aligned
+ * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms.
+ */
+#ifndef ETH_PAD_SIZE
+#define ETH_PAD_SIZE 0
+#endif
+
+/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table
+ * entries (using etharp_add_static_entry/etharp_remove_static_entry).
+ */
+#ifndef ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_SUPPORT_STATIC_ENTRIES 0
+#endif
+
+
+/*
+ --------------------------------
+ ---------- IP options ----------
+ --------------------------------
+*/
+/**
+ * IP_FORWARD==1: Enables the ability to forward IP packets across network
+ * interfaces. If you are going to run lwIP on a device with only one network
+ * interface, define this to 0.
+ */
+#ifndef IP_FORWARD
+#define IP_FORWARD 0
+#endif
+
+/**
+ * IP_OPTIONS_ALLOWED: Defines the behavior for IP options.
+ * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped.
+ * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed).
+ */
+#ifndef IP_OPTIONS_ALLOWED
+#define IP_OPTIONS_ALLOWED 1
+#endif
+
+/**
+ * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that
+ * this option does not affect outgoing packet sizes, which can be controlled
+ * via IP_FRAG.
+ */
+#ifndef IP_REASSEMBLY
+#define IP_REASSEMBLY 1
+#endif
+
+/**
+ * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note
+ * that this option does not affect incoming packet sizes, which can be
+ * controlled via IP_REASSEMBLY.
+ */
+#ifndef IP_FRAG
+#define IP_FRAG 1
+#endif
+
+/**
+ * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally)
+ * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived
+ * in this time, the whole packet is discarded.
+ */
+#ifndef IP_REASS_MAXAGE
+#define IP_REASS_MAXAGE 3
+#endif
+
+/**
+ * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled.
+ * Since the received pbufs are enqueued, be sure to configure
+ * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive
+ * packets even if the maximum amount of fragments is enqueued for reassembly!
+ */
+#ifndef IP_REASS_MAX_PBUFS
+#define IP_REASS_MAX_PBUFS 10
+#endif
+
+/**
+ * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP
+ * fragmentation. Otherwise pbufs are allocated and reference the original
+ * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1,
+ * new PBUF_RAM pbufs are used for fragments).
+ * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs!
+ */
+#ifndef IP_FRAG_USES_STATIC_BUF
+#define IP_FRAG_USES_STATIC_BUF 0
+#endif
+
+/**
+ * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer
+ * (requires IP_FRAG_USES_STATIC_BUF==1)
+ */
+#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU)
+#define IP_FRAG_MAX_MTU 1500
+#endif
+
+/**
+ * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers.
+ */
+#ifndef IP_DEFAULT_TTL
+#define IP_DEFAULT_TTL 255
+#endif
+
+/**
+ * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast
+ * filter per pcb on udp and raw send operations. To enable broadcast filter
+ * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1.
+ */
+#ifndef IP_SOF_BROADCAST
+#define IP_SOF_BROADCAST 0
+#endif
+
+/**
+ * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast
+ * filter on recv operations.
+ */
+#ifndef IP_SOF_BROADCAST_RECV
+#define IP_SOF_BROADCAST_RECV 0
+#endif
+
+/*
+ ----------------------------------
+ ---------- ICMP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_ICMP==1: Enable ICMP module inside the IP stack.
+ * Be careful, disable that make your product non-compliant to RFC1122
+ */
+#ifndef LWIP_ICMP
+#define LWIP_ICMP 1
+#endif
+
+/**
+ * ICMP_TTL: Default value for Time-To-Live used by ICMP packets.
+ */
+#ifndef ICMP_TTL
+#define ICMP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only)
+ */
+#ifndef LWIP_BROADCAST_PING
+#define LWIP_BROADCAST_PING 0
+#endif
+
+/**
+ * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only)
+ */
+#ifndef LWIP_MULTICAST_PING
+#define LWIP_MULTICAST_PING 0
+#endif
+
+/*
+ ---------------------------------
+ ---------- RAW options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#ifndef LWIP_RAW
+#define LWIP_RAW 1
+#endif
+
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#ifndef RAW_TTL
+#define RAW_TTL (IP_DEFAULT_TTL)
+#endif
+
+/*
+ ----------------------------------
+ ---------- DHCP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_DHCP==1: Enable DHCP module.
+ */
+#ifndef LWIP_DHCP
+#define LWIP_DHCP 0
+#endif
+
+/**
+ * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address.
+ */
+#ifndef DHCP_DOES_ARP_CHECK
+#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP))
+#endif
+
+/*
+ ------------------------------------
+ ---------- AUTOIP options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_AUTOIP==1: Enable AUTOIP module.
+ */
+#ifndef LWIP_AUTOIP
+#define LWIP_AUTOIP 0
+#endif
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on
+ * the same interface at the same time.
+ */
+#ifndef LWIP_DHCP_AUTOIP_COOP
+#define LWIP_DHCP_AUTOIP_COOP 0
+#endif
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes
+ * that should be sent before falling back on AUTOIP. This can be set
+ * as low as 1 to get an AutoIP address very quickly, but you should
+ * be prepared to handle a changing IP address when DHCP overrides
+ * AutoIP.
+ */
+#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES
+#define LWIP_DHCP_AUTOIP_COOP_TRIES 9
+#endif
+
+/*
+ ----------------------------------
+ ---------- SNMP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP
+ * transport.
+ */
+#ifndef LWIP_SNMP
+#define LWIP_SNMP 0
+#endif
+
+/**
+ * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will
+ * allow. At least one request buffer is required.
+ * Does not have to be changed unless external MIBs answer request asynchronously
+ */
+#ifndef SNMP_CONCURRENT_REQUESTS
+#define SNMP_CONCURRENT_REQUESTS 1
+#endif
+
+/**
+ * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap
+ * destination is required
+ */
+#ifndef SNMP_TRAP_DESTINATIONS
+#define SNMP_TRAP_DESTINATIONS 1
+#endif
+
+/**
+ * SNMP_PRIVATE_MIB:
+ * When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB.
+ */
+#ifndef SNMP_PRIVATE_MIB
+#define SNMP_PRIVATE_MIB 0
+#endif
+
+/**
+ * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not
+ * a safe action and disabled when SNMP_SAFE_REQUESTS = 1).
+ * Unsafe requests are disabled by default!
+ */
+#ifndef SNMP_SAFE_REQUESTS
+#define SNMP_SAFE_REQUESTS 1
+#endif
+
+/**
+ * The maximum length of strings used. This affects the size of
+ * MEMP_SNMP_VALUE elements.
+ */
+#ifndef SNMP_MAX_OCTET_STRING_LEN
+#define SNMP_MAX_OCTET_STRING_LEN 127
+#endif
+
+/**
+ * The maximum depth of the SNMP tree.
+ * With private MIBs enabled, this depends on your MIB!
+ * This affects the size of MEMP_SNMP_VALUE elements.
+ */
+#ifndef SNMP_MAX_TREE_DEPTH
+#define SNMP_MAX_TREE_DEPTH 15
+#endif
+
+/**
+ * The size of the MEMP_SNMP_VALUE elements, normally calculated from
+ * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH.
+ */
+#ifndef SNMP_MAX_VALUE_SIZE
+#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH))
+#endif
+
+/*
+ ----------------------------------
+ ---------- IGMP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_IGMP==1: Turn on IGMP module.
+ */
+#ifndef LWIP_IGMP
+#define LWIP_IGMP 0
+#endif
+
+/*
+ ----------------------------------
+ ---------- DNS options -----------
+ ----------------------------------
+*/
+/**
+ * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
+ * transport.
+ */
+#ifndef LWIP_DNS
+#define LWIP_DNS 0
+#endif
+
+/** DNS maximum number of entries to maintain locally. */
+#ifndef DNS_TABLE_SIZE
+#define DNS_TABLE_SIZE 4
+#endif
+
+/** DNS maximum host name length supported in the name table. */
+#ifndef DNS_MAX_NAME_LENGTH
+#define DNS_MAX_NAME_LENGTH 256
+#endif
+
+/** The maximum of DNS servers */
+#ifndef DNS_MAX_SERVERS
+#define DNS_MAX_SERVERS 2
+#endif
+
+/** DNS do a name checking between the query and the response. */
+#ifndef DNS_DOES_NAME_CHECK
+#define DNS_DOES_NAME_CHECK 1
+#endif
+
+/** DNS message max. size. Default value is RFC compliant. */
+#ifndef DNS_MSG_SIZE
+#define DNS_MSG_SIZE 512
+#endif
+
+/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled,
+ * you have to define
+ * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}}
+ * (an array of structs name/address, where address is an u32_t in network
+ * byte order).
+ *
+ * Instead, you can also use an external function:
+ * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name)
+ * that returns the IP address or INADDR_NONE if not found.
+ */
+#ifndef DNS_LOCAL_HOSTLIST
+#define DNS_LOCAL_HOSTLIST 0
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/** If this is turned on, the local host-list can be dynamically changed
+ * at runtime. */
+#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/*
+ ---------------------------------
+ ---------- UDP options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_UDP==1: Turn on UDP.
+ */
+#ifndef LWIP_UDP
+#define LWIP_UDP 1
+#endif
+
+/**
+ * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP)
+ */
+#ifndef LWIP_UDPLITE
+#define LWIP_UDPLITE 0
+#endif
+
+/**
+ * UDP_TTL: Default Time-To-Live value.
+ */
+#ifndef UDP_TTL
+#define UDP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf.
+ */
+#ifndef LWIP_NETBUF_RECVINFO
+#define LWIP_NETBUF_RECVINFO 0
+#endif
+
+/*
+ ---------------------------------
+ ---------- TCP options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_TCP==1: Turn on TCP.
+ */
+#ifndef LWIP_TCP
+#define LWIP_TCP 1
+#endif
+
+/**
+ * TCP_TTL: Default Time-To-Live value.
+ */
+#ifndef TCP_TTL
+#define TCP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * TCP_WND: The size of a TCP window. This must be at least
+ * (2 * TCP_MSS) for things to work well
+ */
+#ifndef TCP_WND
+#define TCP_WND (4 * TCP_MSS)
+#endif
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#ifndef TCP_MAXRTX
+#define TCP_MAXRTX 12
+#endif
+
+/**
+ * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+ */
+#ifndef TCP_SYNMAXRTX
+#define TCP_SYNMAXRTX 6
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#ifndef TCP_QUEUE_OOSEQ
+#define TCP_QUEUE_OOSEQ (LWIP_TCP)
+#endif
+
+/**
+ * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
+ * you might want to increase this.)
+ * For the receive side, this MSS is advertised to the remote side
+ * when opening a connection. For the transmit size, this MSS sets
+ * an upper limit on the MSS advertised by the remote host.
+ */
+#ifndef TCP_MSS
+#define TCP_MSS 536
+#endif
+
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#ifndef TCP_CALCULATE_EFF_SEND_MSS
+#define TCP_CALCULATE_EFF_SEND_MSS 1
+#endif
+
+
+/**
+ * TCP_SND_BUF: TCP sender buffer space (bytes).
+ */
+#ifndef TCP_SND_BUF
+#define TCP_SND_BUF 256
+#endif
+
+/**
+ * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#ifndef TCP_SND_QUEUELEN
+#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
+#endif
+
+/**
+ * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than
+ * TCP_SND_BUF. It is the amount of space which must be available in the
+ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
+ */
+#ifndef TCP_SNDLOWAT
+#define TCP_SNDLOWAT ((TCP_SND_BUF)/2)
+#endif
+
+/**
+ * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater
+ * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
+ * this number, select returns writable (combined with TCP_SNDLOWAT).
+ */
+#ifndef TCP_SNDQUEUELOWAT
+#define TCP_SNDQUEUELOWAT ((TCP_SND_QUEUELEN)/2)
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#ifndef TCP_LISTEN_BACKLOG
+#define TCP_LISTEN_BACKLOG 0
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#ifndef TCP_DEFAULT_LISTEN_BACKLOG
+#define TCP_DEFAULT_LISTEN_BACKLOG 0xff
+#endif
+
+/**
+ * TCP_OVERSIZE: The maximum number of bytes that tcp_write may
+ * allocate ahead of time in an attempt to create shorter pbuf chains
+ * for transmission. The meaningful range is 0 to TCP_MSS. Some
+ * suggested values are:
+ *
+ * 0: Disable oversized allocation. Each tcp_write() allocates a new
+ pbuf (old behaviour).
+ * 1: Allocate size-aligned pbufs with minimal excess. Use this if your
+ * scatter-gather DMA requires aligned fragments.
+ * 128: Limit the pbuf/memory overhead to 20%.
+ * TCP_MSS: Try to create unfragmented TCP packets.
+ * TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
+ */
+#ifndef TCP_OVERSIZE
+#define TCP_OVERSIZE TCP_MSS
+#endif
+
+/**
+ * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
+ */
+#ifndef LWIP_TCP_TIMESTAMPS
+#define LWIP_TCP_TIMESTAMPS 0
+#endif
+
+/**
+ * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
+ * explicit window update
+ */
+#ifndef TCP_WND_UPDATE_THRESHOLD
+#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4)
+#endif
+
+/**
+ * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
+ * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
+ * events (accept, sent, etc) that happen in the system.
+ * LWIP_CALLBACK_API==1: The PCB callback function is called directly
+ * for the event.
+ */
+#ifndef LWIP_EVENT_API
+#define LWIP_EVENT_API 0
+#define LWIP_CALLBACK_API 1
+#else
+#define LWIP_EVENT_API 1
+#define LWIP_CALLBACK_API 0
+#endif
+
+
+/*
+ ----------------------------------
+ ---------- Pbuf options ----------
+ ----------------------------------
+*/
+/**
+ * PBUF_LINK_HLEN: the number of bytes that should be allocated for a
+ * link level header. The default is 14, the standard value for
+ * Ethernet.
+ */
+#ifndef PBUF_LINK_HLEN
+#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE)
+#endif
+
+/**
+ * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
+ * designed to accomodate single full size TCP frame in one pbuf, including
+ * TCP_MSS, IP header, and link header.
+ */
+#ifndef PBUF_POOL_BUFSIZE
+#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)
+#endif
+
+/*
+ ------------------------------------------------
+ ---------- Network Interfaces options ----------
+ ------------------------------------------------
+*/
+/**
+ * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname
+ * field.
+ */
+#ifndef LWIP_NETIF_HOSTNAME
+#define LWIP_NETIF_HOSTNAME 0
+#endif
+
+/**
+ * LWIP_NETIF_API==1: Support netif api (in netifapi.c)
+ */
+#ifndef LWIP_NETIF_API
+#define LWIP_NETIF_API 0
+#endif
+
+/**
+ * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface
+ * changes its up/down status (i.e., due to DHCP IP acquistion)
+ */
+#ifndef LWIP_NETIF_STATUS_CALLBACK
+#define LWIP_NETIF_STATUS_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
+ * whenever the link changes (i.e., link down)
+ */
+#ifndef LWIP_NETIF_LINK_CALLBACK
+#define LWIP_NETIF_LINK_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table
+ * indices) in struct netif. TCP and UDP can make use of this to prevent
+ * scanning the ARP table for every sent packet. While this is faster for big
+ * ARP tables or many concurrent connections, it might be counterproductive
+ * if you have a tiny ARP table or if there never are concurrent connections.
+ */
+#ifndef LWIP_NETIF_HWADDRHINT
+#define LWIP_NETIF_HWADDRHINT 0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP
+ * address equal to the netif IP address, looping them back up the stack.
+ */
+#ifndef LWIP_NETIF_LOOPBACK
+#define LWIP_NETIF_LOOPBACK 0
+#endif
+
+/**
+ * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback
+ * sending for each netif (0 = disabled)
+ */
+#ifndef LWIP_LOOPBACK_MAX_PBUFS
+#define LWIP_LOOPBACK_MAX_PBUFS 0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in
+ * the system, as netifs must change how they behave depending on this setting
+ * for the LWIP_NETIF_LOOPBACK option to work.
+ * Setting this is needed to avoid reentering non-reentrant functions like
+ * tcp_input().
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a
+ * multithreaded environment like tcpip.c. In this case, netif->input()
+ * is called directly.
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup.
+ * The packets are put on a list and netif_poll() must be called in
+ * the main application loop.
+ */
+#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING
+#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS)
+#endif
+
+/**
+ * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data
+ * to be sent into one single pbuf. This is for compatibility with DMA-enabled
+ * MACs that do not support scatter-gather.
+ * Beware that this might involve CPU-memcpy before transmitting that would not
+ * be needed without this flag! Use this only if you need to!
+ *
+ * @todo: TCP and IP-frag do not work with this, yet:
+ */
+#ifndef LWIP_NETIF_TX_SINGLE_PBUF
+#define LWIP_NETIF_TX_SINGLE_PBUF 0
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+/*
+ ------------------------------------
+ ---------- LOOPIF options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c
+ */
+#ifndef LWIP_HAVE_LOOPIF
+#define LWIP_HAVE_LOOPIF 0
+#endif
+
+/*
+ ------------------------------------
+ ---------- SLIPIF options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c
+ */
+#ifndef LWIP_HAVE_SLIPIF
+#define LWIP_HAVE_SLIPIF 0
+#endif
+
+/*
+ ------------------------------------
+ ---------- Thread options ----------
+ ------------------------------------
+*/
+/**
+ * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread.
+ */
+#ifndef TCPIP_THREAD_NAME
+#define TCPIP_THREAD_NAME "tcpip_thread"
+#endif
+
+/**
+ * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef TCPIP_THREAD_STACKSIZE
+#define TCPIP_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef TCPIP_THREAD_PRIO
+#define TCPIP_THREAD_PRIO 1
+#endif
+
+/**
+ * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when tcpip_init is called.
+ */
+#ifndef TCPIP_MBOX_SIZE
+#define TCPIP_MBOX_SIZE 0
+#endif
+
+/**
+ * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread.
+ */
+#ifndef SLIPIF_THREAD_NAME
+#define SLIPIF_THREAD_NAME "slipif_loop"
+#endif
+
+/**
+ * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef SLIPIF_THREAD_STACKSIZE
+#define SLIPIF_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef SLIPIF_THREAD_PRIO
+#define SLIPIF_THREAD_PRIO 1
+#endif
+
+/**
+ * PPP_THREAD_NAME: The name assigned to the pppInputThread.
+ */
+#ifndef PPP_THREAD_NAME
+#define PPP_THREAD_NAME "pppInputThread"
+#endif
+
+/**
+ * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef PPP_THREAD_STACKSIZE
+#define PPP_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * PPP_THREAD_PRIO: The priority assigned to the pppInputThread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef PPP_THREAD_PRIO
+#define PPP_THREAD_PRIO 1
+#endif
+
+/**
+ * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread.
+ */
+#ifndef DEFAULT_THREAD_NAME
+#define DEFAULT_THREAD_NAME "lwIP"
+#endif
+
+/**
+ * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef DEFAULT_THREAD_STACKSIZE
+#define DEFAULT_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef DEFAULT_THREAD_PRIO
+#define DEFAULT_THREAD_PRIO 1
+#endif
+
+/**
+ * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#ifndef DEFAULT_RAW_RECVMBOX_SIZE
+#define DEFAULT_RAW_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#ifndef DEFAULT_UDP_RECVMBOX_SIZE
+#define DEFAULT_UDP_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#ifndef DEFAULT_TCP_RECVMBOX_SIZE
+#define DEFAULT_TCP_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections.
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when the acceptmbox is created.
+ */
+#ifndef DEFAULT_ACCEPTMBOX_SIZE
+#define DEFAULT_ACCEPTMBOX_SIZE 0
+#endif
+
+/*
+ ----------------------------------------------
+ ---------- Sequential layer options ----------
+ ----------------------------------------------
+*/
+/**
+ * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!)
+ * Don't use it if you're not an active lwIP project member
+ */
+#ifndef LWIP_TCPIP_CORE_LOCKING
+#define LWIP_TCPIP_CORE_LOCKING 0
+#endif
+
+/**
+ * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!)
+ * Don't use it if you're not an active lwIP project member
+ */
+#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 0
+#endif
+
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#ifndef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#endif
+
+/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create
+ * timers running in tcpip_thread from another thread.
+ */
+#ifndef LWIP_TCPIP_TIMEOUT
+#define LWIP_TCPIP_TIMEOUT 1
+#endif
+
+/*
+ ------------------------------------
+ ---------- Socket options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#ifndef LWIP_SOCKET
+#define LWIP_SOCKET 1
+#endif
+
+/**
+ * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names.
+ * (only used if you use sockets.c)
+ */
+#ifndef LWIP_COMPAT_SOCKETS
+#define LWIP_COMPAT_SOCKETS 1
+#endif
+
+/**
+ * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names.
+ * Disable this option if you use a POSIX operating system that uses the same
+ * names (read, write & close). (only used if you use sockets.c)
+ */
+#ifndef LWIP_POSIX_SOCKETS_IO_NAMES
+#define LWIP_POSIX_SOCKETS_IO_NAMES 1
+#endif
+
+/**
+ * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
+ * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set
+ * in seconds. (does not require sockets.c, and will affect tcp.c)
+ */
+#ifndef LWIP_TCP_KEEPALIVE
+#define LWIP_TCP_KEEPALIVE 0
+#endif
+
+/**
+ * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing.
+ */
+#ifndef LWIP_SO_RCVTIMEO
+#define LWIP_SO_RCVTIMEO 0
+#endif
+
+/**
+ * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
+ */
+#ifndef LWIP_SO_RCVBUF
+#define LWIP_SO_RCVBUF 0
+#endif
+
+/**
+ * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize.
+ */
+#ifndef RECV_BUFSIZE_DEFAULT
+#define RECV_BUFSIZE_DEFAULT INT_MAX
+#endif
+
+/**
+ * SO_REUSE==1: Enable SO_REUSEADDR option.
+ */
+#ifndef SO_REUSE
+#define SO_REUSE 0
+#endif
+
+/**
+ * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
+ * to all local matches if SO_REUSEADDR is turned on.
+ * WARNING: Adds a memcpy for every packet if passing to more than one pcb!
+ */
+#ifndef SO_REUSE_RXTOALL
+#define SO_REUSE_RXTOALL 0
+#endif
+
+/*
+ ----------------------------------------
+ ---------- Statistics options ----------
+ ----------------------------------------
+*/
+/**
+ * LWIP_STATS==1: Enable statistics collection in lwip_stats.
+ */
+#ifndef LWIP_STATS
+#define LWIP_STATS 1
+#endif
+
+#if LWIP_STATS
+
+/**
+ * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
+ */
+#ifndef LWIP_STATS_DISPLAY
+#define LWIP_STATS_DISPLAY 0
+#endif
+
+/**
+ * LINK_STATS==1: Enable link stats.
+ */
+#ifndef LINK_STATS
+#define LINK_STATS 1
+#endif
+
+/**
+ * ETHARP_STATS==1: Enable etharp stats.
+ */
+#ifndef ETHARP_STATS
+#define ETHARP_STATS (LWIP_ARP)
+#endif
+
+/**
+ * IP_STATS==1: Enable IP stats.
+ */
+#ifndef IP_STATS
+#define IP_STATS 1
+#endif
+
+/**
+ * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is
+ * on if using either frag or reass.
+ */
+#ifndef IPFRAG_STATS
+#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG)
+#endif
+
+/**
+ * ICMP_STATS==1: Enable ICMP stats.
+ */
+#ifndef ICMP_STATS
+#define ICMP_STATS 1
+#endif
+
+/**
+ * IGMP_STATS==1: Enable IGMP stats.
+ */
+#ifndef IGMP_STATS
+#define IGMP_STATS (LWIP_IGMP)
+#endif
+
+/**
+ * UDP_STATS==1: Enable UDP stats. Default is on if
+ * UDP enabled, otherwise off.
+ */
+#ifndef UDP_STATS
+#define UDP_STATS (LWIP_UDP)
+#endif
+
+/**
+ * TCP_STATS==1: Enable TCP stats. Default is on if TCP
+ * enabled, otherwise off.
+ */
+#ifndef TCP_STATS
+#define TCP_STATS (LWIP_TCP)
+#endif
+
+/**
+ * MEM_STATS==1: Enable mem.c stats.
+ */
+#ifndef MEM_STATS
+#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0))
+#endif
+
+/**
+ * MEMP_STATS==1: Enable memp.c pool stats.
+ */
+#ifndef MEMP_STATS
+#define MEMP_STATS (MEMP_MEM_MALLOC == 0)
+#endif
+
+/**
+ * SYS_STATS==1: Enable system stats (sem and mbox counts, etc).
+ */
+#ifndef SYS_STATS
+#define SYS_STATS (NO_SYS == 0)
+#endif
+
+#else
+
+#define LINK_STATS 0
+#define IP_STATS 0
+#define IPFRAG_STATS 0
+#define ICMP_STATS 0
+#define IGMP_STATS 0
+#define UDP_STATS 0
+#define TCP_STATS 0
+#define MEM_STATS 0
+#define MEMP_STATS 0
+#define SYS_STATS 0
+#define LWIP_STATS_DISPLAY 0
+
+#endif /* LWIP_STATS */
+
+/*
+ ---------------------------------
+ ---------- PPP options ----------
+ ---------------------------------
+*/
+/**
+ * PPP_SUPPORT==1: Enable PPP.
+ */
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT 0
+#endif
+
+/**
+ * PPPOE_SUPPORT==1: Enable PPP Over Ethernet
+ */
+#ifndef PPPOE_SUPPORT
+#define PPPOE_SUPPORT 0
+#endif
+
+/**
+ * PPPOS_SUPPORT==1: Enable PPP Over Serial
+ */
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT PPP_SUPPORT
+#endif
+
+#if PPP_SUPPORT
+
+/**
+ * NUM_PPP: Max PPP sessions.
+ */
+#ifndef NUM_PPP
+#define NUM_PPP 1
+#endif
+
+/**
+ * PAP_SUPPORT==1: Support PAP.
+ */
+#ifndef PAP_SUPPORT
+#define PAP_SUPPORT 0
+#endif
+
+/**
+ * CHAP_SUPPORT==1: Support CHAP.
+ */
+#ifndef CHAP_SUPPORT
+#define CHAP_SUPPORT 0
+#endif
+
+/**
+ * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef MSCHAP_SUPPORT
+#define MSCHAP_SUPPORT 0
+#endif
+
+/**
+ * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef CBCP_SUPPORT
+#define CBCP_SUPPORT 0
+#endif
+
+/**
+ * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef CCP_SUPPORT
+#define CCP_SUPPORT 0
+#endif
+
+/**
+ * VJ_SUPPORT==1: Support VJ header compression.
+ */
+#ifndef VJ_SUPPORT
+#define VJ_SUPPORT 0
+#endif
+
+/**
+ * MD5_SUPPORT==1: Support MD5 (see also CHAP).
+ */
+#ifndef MD5_SUPPORT
+#define MD5_SUPPORT 0
+#endif
+
+/*
+ * Timeouts
+ */
+#ifndef FSM_DEFTIMEOUT
+#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */
+#endif
+
+#ifndef FSM_DEFMAXTERMREQS
+#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#endif
+
+#ifndef FSM_DEFMAXCONFREQS
+#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#endif
+
+#ifndef FSM_DEFMAXNAKLOOPS
+#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+#endif
+
+#ifndef UPAP_DEFTIMEOUT
+#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */
+#endif
+
+#ifndef UPAP_DEFREQTIME
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+#endif
+
+#ifndef CHAP_DEFTIMEOUT
+#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */
+#endif
+
+#ifndef CHAP_DEFTRANSMITS
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+#endif
+
+/* Interval in seconds between keepalive echo requests, 0 to disable. */
+#ifndef LCP_ECHOINTERVAL
+#define LCP_ECHOINTERVAL 0
+#endif
+
+/* Number of unanswered echo requests before failure. */
+#ifndef LCP_MAXECHOFAILS
+#define LCP_MAXECHOFAILS 3
+#endif
+
+/* Max Xmit idle time (in jiffies) before resend flag char. */
+#ifndef PPP_MAXIDLEFLAG
+#define PPP_MAXIDLEFLAG 100
+#endif
+
+/*
+ * Packet sizes
+ *
+ * Note - lcp shouldn't be allowed to negotiate stuff outside these
+ * limits. See lcp.h in the pppd directory.
+ * (XXX - these constants should simply be shared by lcp.c instead
+ * of living in lcp.h)
+ */
+#define PPP_MTU 1500 /* Default MTU (size of Info field) */
+#ifndef PPP_MAXMTU
+/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */
+#define PPP_MAXMTU 1500 /* Largest MTU we allow */
+#endif
+#define PPP_MINMTU 64
+#define PPP_MRU 1500 /* default MRU = max length of info field */
+#define PPP_MAXMRU 1500 /* Largest MRU we allow */
+#ifndef PPP_DEFMRU
+#define PPP_DEFMRU 296 /* Try for this */
+#endif
+#define PPP_MINMRU 128 /* No MRUs below this */
+
+#ifndef MAXNAMELEN
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#endif
+#ifndef MAXSECRETLEN
+#define MAXSECRETLEN 256 /* max length of password or secret */
+#endif
+
+#endif /* PPP_SUPPORT */
+
+/*
+ --------------------------------------
+ ---------- Checksum options ----------
+ --------------------------------------
+*/
+/**
+ * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.
+ */
+#ifndef CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
+ */
+#ifndef CHECKSUM_GEN_UDP
+#define CHECKSUM_GEN_UDP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
+ */
+#ifndef CHECKSUM_GEN_TCP
+#define CHECKSUM_GEN_TCP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
+ */
+#ifndef CHECKSUM_CHECK_IP
+#define CHECKSUM_CHECK_IP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
+ */
+#ifndef CHECKSUM_CHECK_UDP
+#define CHECKSUM_CHECK_UDP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.
+ */
+#ifndef CHECKSUM_CHECK_TCP
+#define CHECKSUM_CHECK_TCP 1
+#endif
+
+/**
+ * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from
+ * application buffers to pbufs.
+ */
+#ifndef LWIP_CHECKSUM_ON_COPY
+#define LWIP_CHECKSUM_ON_COPY 0
+#endif
+
+/*
+ ---------------------------------------
+ ---------- Debugging options ----------
+ ---------------------------------------
+*/
+/**
+ * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is
+ * compared against this value. If it is smaller, then debugging
+ * messages are written.
+ */
+#ifndef LWIP_DBG_MIN_LEVEL
+#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
+#endif
+
+/**
+ * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
+ * debug messages of certain types.
+ */
+#ifndef LWIP_DBG_TYPES_ON
+#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
+#endif
+
+/**
+ * ETHARP_DEBUG: Enable debugging in etharp.c.
+ */
+#ifndef ETHARP_DEBUG
+#define ETHARP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * NETIF_DEBUG: Enable debugging in netif.c.
+ */
+#ifndef NETIF_DEBUG
+#define NETIF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UNDIIF_DEBUG: Enable debugging in undiif.c.
+ */
+#ifndef UNDIIF_DEBUG
+#define UNDIIF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UNDIIF_ARP_DEBUG: Enable ETHARP debugging in undiif.c.
+ */
+#ifndef UNDIIF_ARP_DEBUG
+#define UNDIIF_ARP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UNDIIF_NET_DEBUG: Enable NETIF debugging in undiif.c.
+ */
+#ifndef UNDIIF_NET_DEBUG
+#define UNDIIF_NET_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UNDIIF_ID_DEBUG: Enable debugging to identify packets in undiif.c.
+ */
+#ifndef UNDIIF_ID_DEBUG
+#define UNDIIF_ID_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * PBUF_DEBUG: Enable debugging in pbuf.c.
+ */
+#ifndef PBUF_DEBUG
+#define PBUF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * API_LIB_DEBUG: Enable debugging in api_lib.c.
+ */
+#ifndef API_LIB_DEBUG
+#define API_LIB_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * API_MSG_DEBUG: Enable debugging in api_msg.c.
+ */
+#ifndef API_MSG_DEBUG
+#define API_MSG_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SOCKETS_DEBUG: Enable debugging in sockets.c.
+ */
+#ifndef SOCKETS_DEBUG
+#define SOCKETS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * ICMP_DEBUG: Enable debugging in icmp.c.
+ */
+#ifndef ICMP_DEBUG
+#define ICMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IGMP_DEBUG: Enable debugging in igmp.c.
+ */
+#ifndef IGMP_DEBUG
+#define IGMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * INET_DEBUG: Enable debugging in inet.c.
+ */
+#ifndef INET_DEBUG
+#define INET_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_DEBUG: Enable debugging for IP.
+ */
+#ifndef IP_DEBUG
+#define IP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass.
+ */
+#ifndef IP_REASS_DEBUG
+#define IP_REASS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * RAW_DEBUG: Enable debugging in raw.c.
+ */
+#ifndef RAW_DEBUG
+#define RAW_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * MEM_DEBUG: Enable debugging in mem.c.
+ */
+#ifndef MEM_DEBUG
+#define MEM_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * MEMP_DEBUG: Enable debugging in memp.c.
+ */
+#ifndef MEMP_DEBUG
+#define MEMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SYS_DEBUG: Enable debugging in sys.c.
+ */
+#ifndef SYS_DEBUG
+#define SYS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TIMERS_DEBUG: Enable debugging in timers.c.
+ */
+#ifndef TIMERS_DEBUG
+#define TIMERS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_DEBUG: Enable debugging for TCP.
+ */
+#ifndef TCP_DEBUG
+#define TCP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug.
+ */
+#ifndef TCP_INPUT_DEBUG
+#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit.
+ */
+#ifndef TCP_FR_DEBUG
+#define TCP_FR_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit
+ * timeout.
+ */
+#ifndef TCP_RTO_DEBUG
+#define TCP_RTO_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_CWND_DEBUG: Enable debugging for TCP congestion window.
+ */
+#ifndef TCP_CWND_DEBUG
+#define TCP_CWND_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating.
+ */
+#ifndef TCP_WND_DEBUG
+#define TCP_WND_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions.
+ */
+#ifndef TCP_OUTPUT_DEBUG
+#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RST_DEBUG: Enable debugging for TCP with the RST message.
+ */
+#ifndef TCP_RST_DEBUG
+#define TCP_RST_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths.
+ */
+#ifndef TCP_QLEN_DEBUG
+#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UDP_DEBUG: Enable debugging in UDP.
+ */
+#ifndef UDP_DEBUG
+#define UDP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCPIP_DEBUG: Enable debugging in tcpip.c.
+ */
+#ifndef TCPIP_DEBUG
+#define TCPIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * PPP_DEBUG: Enable debugging for PPP.
+ */
+#ifndef PPP_DEBUG
+#define PPP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SLIP_DEBUG: Enable debugging in slipif.c.
+ */
+#ifndef SLIP_DEBUG
+#define SLIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * DHCP_DEBUG: Enable debugging in dhcp.c.
+ */
+#ifndef DHCP_DEBUG
+#define DHCP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * AUTOIP_DEBUG: Enable debugging in autoip.c.
+ */
+#ifndef AUTOIP_DEBUG
+#define AUTOIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MSG_DEBUG: Enable debugging for SNMP messages.
+ */
+#ifndef SNMP_MSG_DEBUG
+#define SNMP_MSG_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs.
+ */
+#ifndef SNMP_MIB_DEBUG
+#define SNMP_MIB_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * DNS_DEBUG: Enable debugging for DNS.
+ */
+#ifndef DNS_DEBUG
+#define DNS_DEBUG LWIP_DBG_OFF
+#endif
+
+#endif /* __LWIP_OPT_H__ */
diff --git a/core/lwip/src/include/lwip/pbuf.h b/core/lwip/src/include/lwip/pbuf.h
new file mode 100644
index 00000000..3b1d608b
--- /dev/null
+++ b/core/lwip/src/include/lwip/pbuf.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __LWIP_PBUF_H__
+#define __LWIP_PBUF_H__
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Currently, the pbuf_custom code is only needed for one specific configuration
+ * of IP_FRAG */
+#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF)
+
+#define PBUF_TRANSPORT_HLEN 20
+#define PBUF_IP_HLEN 20
+
+typedef enum {
+ PBUF_TRANSPORT,
+ PBUF_IP,
+ PBUF_LINK,
+ PBUF_RAW
+} pbuf_layer;
+
+typedef enum {
+ PBUF_RAM, /* pbuf data is stored in RAM */
+ PBUF_ROM, /* pbuf data is stored in ROM */
+ PBUF_REF, /* pbuf comes from the pbuf pool */
+ PBUF_POOL /* pbuf payload refers to RAM */
+} pbuf_type;
+
+
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH 0x01U
+/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a
+ a pbuf differently */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+/** indicates this pbuf is UDP multicast to be looped back */
+#define PBUF_FLAG_MCASTLOOP 0x04U
+
+struct pbuf {
+ /** next pbuf in singly linked pbuf chain */
+ struct pbuf *next;
+
+ /** pointer to the actual data in the buffer */
+ void *payload;
+
+ /**
+ * total length of this buffer and all next buffers in chain
+ * belonging to the same packet.
+ *
+ * For non-queue packet chains this is the invariant:
+ * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
+ */
+ u16_t tot_len;
+
+ /** length of this buffer */
+ u16_t len;
+
+ /** pbuf_type as u8_t instead of enum to save space */
+ u8_t /*pbuf_type*/ type;
+
+ /** misc flags */
+ u8_t flags;
+
+ /**
+ * the reference count always equals the number of pointers
+ * that refer to this pbuf. This can be pointers from an application,
+ * the stack itself, or pbuf->next pointers from a chain.
+ */
+ u16_t ref;
+};
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Prototype for a function to free a custom pbuf */
+typedef void (*pbuf_free_custom_fn)(struct pbuf *p);
+
+/** A custom pbuf: like a pbuf, but following a function pointer to free it. */
+struct pbuf_custom {
+ /** The actual pbuf */
+ struct pbuf pbuf;
+ /** This function is called when pbuf_free deallocates this pbuf(_custom) */
+ pbuf_free_custom_fn custom_free_function;
+};
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
+#define pbuf_init()
+
+struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type);
+#if LWIP_SUPPORT_CUSTOM_PBUF
+struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type,
+ struct pbuf_custom *p, void *payload_mem,
+ u16_t payload_mem_len);
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+void pbuf_realloc(struct pbuf *p, u16_t size);
+u8_t pbuf_header(struct pbuf *p, s16_t header_size);
+void pbuf_ref(struct pbuf *p);
+u8_t pbuf_free(struct pbuf *p);
+u8_t pbuf_clen(struct pbuf *p);
+void pbuf_cat(struct pbuf *head, struct pbuf *tail);
+void pbuf_chain(struct pbuf *head, struct pbuf *tail);
+struct pbuf *pbuf_dechain(struct pbuf *p);
+err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from);
+u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
+err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
+struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
+#if LWIP_CHECKSUM_ON_COPY
+err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+u8_t pbuf_get_at(struct pbuf* p, u16_t offset);
+u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n);
+u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
+u16_t pbuf_strstr(struct pbuf* p, const char* substr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_PBUF_H__ */
diff --git a/core/lwip/src/include/lwip/raw.h b/core/lwip/src/include/lwip/raw.h
new file mode 100644
index 00000000..17d0a1c5
--- /dev/null
+++ b/core/lwip/src/include/lwip/raw.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_RAW_H__
+#define __LWIP_RAW_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct raw_pcb;
+
+/** Function prototype for raw pcb receive callback functions.
+ * @param arg user supplied argument (raw_pcb.recv_arg)
+ * @param pcb the raw_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @return 1 if the packet was 'eaten' (aka. deleted),
+ * 0 if the packet lives on
+ * If returning 1, the callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ */
+typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr);
+
+struct raw_pcb {
+ /* Common members of all PCB types */
+ IP_PCB;
+
+ struct raw_pcb *next;
+
+ u8_t protocol;
+
+ /** receive callback function */
+ raw_recv_fn recv;
+ /* user-supplied argument for the recv callback */
+ void *recv_arg;
+};
+
+/* The following functions is the application layer interface to the
+ RAW code. */
+struct raw_pcb * raw_new (u8_t proto);
+void raw_remove (struct raw_pcb *pcb);
+err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr);
+err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr);
+
+void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
+err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr);
+err_t raw_send (struct raw_pcb *pcb, struct pbuf *p);
+
+/* The following functions are the lower layer interface to RAW. */
+u8_t raw_input (struct pbuf *p, struct netif *inp);
+#define raw_init() /* Compatibility define, not init needed. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_RAW */
+
+#endif /* __LWIP_RAW_H__ */
diff --git a/core/lwip/src/include/lwip/sio.h b/core/lwip/src/include/lwip/sio.h
new file mode 100644
index 00000000..28ae2f22
--- /dev/null
+++ b/core/lwip/src/include/lwip/sio.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ */
+
+/*
+ * This is the interface to the platform specific serial IO module
+ * It needs to be implemented by those platforms which need SLIP or PPP
+ */
+
+#ifndef __SIO_H__
+#define __SIO_H__
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If you want to define sio_fd_t elsewhere or differently,
+ define this in your cc.h file. */
+#ifndef __sio_fd_t_defined
+typedef void * sio_fd_t;
+#endif
+
+/* The following functions can be defined to something else in your cc.h file
+ or be implemented in your custom sio.c file. */
+
+#ifndef sio_open
+/**
+ * Opens a serial device for communication.
+ *
+ * @param devnum device number
+ * @return handle to serial device if successful, NULL otherwise
+ */
+sio_fd_t sio_open(u8_t devnum);
+#endif
+
+#ifndef sio_send
+/**
+ * Sends a single character to the serial device.
+ *
+ * @param c character to send
+ * @param fd serial device handle
+ *
+ * @note This function will block until the character can be sent.
+ */
+void sio_send(u8_t c, sio_fd_t fd);
+#endif
+
+#ifndef sio_recv
+/**
+ * Receives a single character from the serial device.
+ *
+ * @param fd serial device handle
+ *
+ * @note This function will block until a character is received.
+ */
+u8_t sio_recv(sio_fd_t fd);
+#endif
+
+#ifndef sio_read
+/**
+ * Reads from the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
+ *
+ * @note This function will block until data can be received. The blocking
+ * can be cancelled by calling sio_read_abort().
+ */
+u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_tryread
+/**
+ * Tries to read from the serial device. Same as sio_read but returns
+ * immediately if no data is available and never blocks.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received
+ */
+u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_write
+/**
+ * Writes to the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data to send
+ * @param len length (in bytes) of data to send
+ * @return number of bytes actually sent
+ *
+ * @note This function will block until all data can be sent.
+ */
+u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_read_abort
+/**
+ * Aborts a blocking sio_read() call.
+ *
+ * @param fd serial device handle
+ */
+void sio_read_abort(sio_fd_t fd);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SIO_H__ */
diff --git a/core/lwip/src/include/lwip/snmp.h b/core/lwip/src/include/lwip/snmp.h
new file mode 100644
index 00000000..2ed043dd
--- /dev/null
+++ b/core/lwip/src/include/lwip/snmp.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ *
+ */
+#ifndef __LWIP_SNMP_H__
+#define __LWIP_SNMP_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/ip_addr.h"
+
+struct udp_pcb;
+struct netif;
+
+/**
+ * @see RFC1213, "MIB-II, 6. Definitions"
+ */
+enum snmp_ifType {
+ snmp_ifType_other=1, /* none of the following */
+ snmp_ifType_regular1822,
+ snmp_ifType_hdh1822,
+ snmp_ifType_ddn_x25,
+ snmp_ifType_rfc877_x25,
+ snmp_ifType_ethernet_csmacd,
+ snmp_ifType_iso88023_csmacd,
+ snmp_ifType_iso88024_tokenBus,
+ snmp_ifType_iso88025_tokenRing,
+ snmp_ifType_iso88026_man,
+ snmp_ifType_starLan,
+ snmp_ifType_proteon_10Mbit,
+ snmp_ifType_proteon_80Mbit,
+ snmp_ifType_hyperchannel,
+ snmp_ifType_fddi,
+ snmp_ifType_lapb,
+ snmp_ifType_sdlc,
+ snmp_ifType_ds1, /* T-1 */
+ snmp_ifType_e1, /* european equiv. of T-1 */
+ snmp_ifType_basicISDN,
+ snmp_ifType_primaryISDN, /* proprietary serial */
+ snmp_ifType_propPointToPointSerial,
+ snmp_ifType_ppp,
+ snmp_ifType_softwareLoopback,
+ snmp_ifType_eon, /* CLNP over IP [11] */
+ snmp_ifType_ethernet_3Mbit,
+ snmp_ifType_nsip, /* XNS over IP */
+ snmp_ifType_slip, /* generic SLIP */
+ snmp_ifType_ultra, /* ULTRA technologies */
+ snmp_ifType_ds3, /* T-3 */
+ snmp_ifType_sip, /* SMDS */
+ snmp_ifType_frame_relay
+};
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** SNMP "sysuptime" Interval */
+#define SNMP_SYSUPTIME_INTERVAL 10
+
+/** fixed maximum length for object identifier type */
+#define LWIP_SNMP_OBJ_ID_LEN 32
+
+/** internal object identifier representation */
+struct snmp_obj_id
+{
+ u8_t len;
+ s32_t id[LWIP_SNMP_OBJ_ID_LEN];
+};
+
+/* system */
+void snmp_set_sysdesr(u8_t* str, u8_t* len);
+void snmp_set_sysobjid(struct snmp_obj_id *oid);
+void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid);
+void snmp_inc_sysuptime(void);
+void snmp_add_sysuptime(u32_t value);
+void snmp_get_sysuptime(u32_t *value);
+void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen);
+void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen);
+void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen);
+
+/* network interface */
+void snmp_add_ifinoctets(struct netif *ni, u32_t value);
+void snmp_inc_ifinucastpkts(struct netif *ni);
+void snmp_inc_ifinnucastpkts(struct netif *ni);
+void snmp_inc_ifindiscards(struct netif *ni);
+void snmp_add_ifoutoctets(struct netif *ni, u32_t value);
+void snmp_inc_ifoutucastpkts(struct netif *ni);
+void snmp_inc_ifoutnucastpkts(struct netif *ni);
+void snmp_inc_ifoutdiscards(struct netif *ni);
+void snmp_inc_iflist(void);
+void snmp_dec_iflist(void);
+
+/* ARP (for atTable and ipNetToMediaTable) */
+void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip);
+void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip);
+
+/* IP */
+void snmp_inc_ipinreceives(void);
+void snmp_inc_ipinhdrerrors(void);
+void snmp_inc_ipinaddrerrors(void);
+void snmp_inc_ipforwdatagrams(void);
+void snmp_inc_ipinunknownprotos(void);
+void snmp_inc_ipindiscards(void);
+void snmp_inc_ipindelivers(void);
+void snmp_inc_ipoutrequests(void);
+void snmp_inc_ipoutdiscards(void);
+void snmp_inc_ipoutnoroutes(void);
+void snmp_inc_ipreasmreqds(void);
+void snmp_inc_ipreasmoks(void);
+void snmp_inc_ipreasmfails(void);
+void snmp_inc_ipfragoks(void);
+void snmp_inc_ipfragfails(void);
+void snmp_inc_ipfragcreates(void);
+void snmp_inc_iproutingdiscards(void);
+void snmp_insert_ipaddridx_tree(struct netif *ni);
+void snmp_delete_ipaddridx_tree(struct netif *ni);
+void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni);
+void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni);
+
+/* ICMP */
+void snmp_inc_icmpinmsgs(void);
+void snmp_inc_icmpinerrors(void);
+void snmp_inc_icmpindestunreachs(void);
+void snmp_inc_icmpintimeexcds(void);
+void snmp_inc_icmpinparmprobs(void);
+void snmp_inc_icmpinsrcquenchs(void);
+void snmp_inc_icmpinredirects(void);
+void snmp_inc_icmpinechos(void);
+void snmp_inc_icmpinechoreps(void);
+void snmp_inc_icmpintimestamps(void);
+void snmp_inc_icmpintimestampreps(void);
+void snmp_inc_icmpinaddrmasks(void);
+void snmp_inc_icmpinaddrmaskreps(void);
+void snmp_inc_icmpoutmsgs(void);
+void snmp_inc_icmpouterrors(void);
+void snmp_inc_icmpoutdestunreachs(void);
+void snmp_inc_icmpouttimeexcds(void);
+void snmp_inc_icmpoutparmprobs(void);
+void snmp_inc_icmpoutsrcquenchs(void);
+void snmp_inc_icmpoutredirects(void);
+void snmp_inc_icmpoutechos(void);
+void snmp_inc_icmpoutechoreps(void);
+void snmp_inc_icmpouttimestamps(void);
+void snmp_inc_icmpouttimestampreps(void);
+void snmp_inc_icmpoutaddrmasks(void);
+void snmp_inc_icmpoutaddrmaskreps(void);
+
+/* TCP */
+void snmp_inc_tcpactiveopens(void);
+void snmp_inc_tcppassiveopens(void);
+void snmp_inc_tcpattemptfails(void);
+void snmp_inc_tcpestabresets(void);
+void snmp_inc_tcpinsegs(void);
+void snmp_inc_tcpoutsegs(void);
+void snmp_inc_tcpretranssegs(void);
+void snmp_inc_tcpinerrs(void);
+void snmp_inc_tcpoutrsts(void);
+
+/* UDP */
+void snmp_inc_udpindatagrams(void);
+void snmp_inc_udpnoports(void);
+void snmp_inc_udpinerrors(void);
+void snmp_inc_udpoutdatagrams(void);
+void snmp_insert_udpidx_tree(struct udp_pcb *pcb);
+void snmp_delete_udpidx_tree(struct udp_pcb *pcb);
+
+/* SNMP */
+void snmp_inc_snmpinpkts(void);
+void snmp_inc_snmpoutpkts(void);
+void snmp_inc_snmpinbadversions(void);
+void snmp_inc_snmpinbadcommunitynames(void);
+void snmp_inc_snmpinbadcommunityuses(void);
+void snmp_inc_snmpinasnparseerrs(void);
+void snmp_inc_snmpintoobigs(void);
+void snmp_inc_snmpinnosuchnames(void);
+void snmp_inc_snmpinbadvalues(void);
+void snmp_inc_snmpinreadonlys(void);
+void snmp_inc_snmpingenerrs(void);
+void snmp_add_snmpintotalreqvars(u8_t value);
+void snmp_add_snmpintotalsetvars(u8_t value);
+void snmp_inc_snmpingetrequests(void);
+void snmp_inc_snmpingetnexts(void);
+void snmp_inc_snmpinsetrequests(void);
+void snmp_inc_snmpingetresponses(void);
+void snmp_inc_snmpintraps(void);
+void snmp_inc_snmpouttoobigs(void);
+void snmp_inc_snmpoutnosuchnames(void);
+void snmp_inc_snmpoutbadvalues(void);
+void snmp_inc_snmpoutgenerrs(void);
+void snmp_inc_snmpoutgetrequests(void);
+void snmp_inc_snmpoutgetnexts(void);
+void snmp_inc_snmpoutsetrequests(void);
+void snmp_inc_snmpoutgetresponses(void);
+void snmp_inc_snmpouttraps(void);
+void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid);
+void snmp_set_snmpenableauthentraps(u8_t *value);
+void snmp_get_snmpenableauthentraps(u8_t *value);
+
+/* LWIP_SNMP support not available */
+/* define everything to be empty */
+#else
+
+/* system */
+#define snmp_set_sysdesr(str, len)
+#define snmp_set_sysobjid(oid);
+#define snmp_get_sysobjid_ptr(oid)
+#define snmp_inc_sysuptime()
+#define snmp_add_sysuptime(value)
+#define snmp_get_sysuptime(value)
+#define snmp_set_syscontact(ocstr, ocstrlen);
+#define snmp_set_sysname(ocstr, ocstrlen);
+#define snmp_set_syslocation(ocstr, ocstrlen);
+
+/* network interface */
+#define snmp_add_ifinoctets(ni,value)
+#define snmp_inc_ifinucastpkts(ni)
+#define snmp_inc_ifinnucastpkts(ni)
+#define snmp_inc_ifindiscards(ni)
+#define snmp_add_ifoutoctets(ni,value)
+#define snmp_inc_ifoutucastpkts(ni)
+#define snmp_inc_ifoutnucastpkts(ni)
+#define snmp_inc_ifoutdiscards(ni)
+#define snmp_inc_iflist()
+#define snmp_dec_iflist()
+
+/* ARP */
+#define snmp_insert_arpidx_tree(ni,ip)
+#define snmp_delete_arpidx_tree(ni,ip)
+
+/* IP */
+#define snmp_inc_ipinreceives()
+#define snmp_inc_ipinhdrerrors()
+#define snmp_inc_ipinaddrerrors()
+#define snmp_inc_ipforwdatagrams()
+#define snmp_inc_ipinunknownprotos()
+#define snmp_inc_ipindiscards()
+#define snmp_inc_ipindelivers()
+#define snmp_inc_ipoutrequests()
+#define snmp_inc_ipoutdiscards()
+#define snmp_inc_ipoutnoroutes()
+#define snmp_inc_ipreasmreqds()
+#define snmp_inc_ipreasmoks()
+#define snmp_inc_ipreasmfails()
+#define snmp_inc_ipfragoks()
+#define snmp_inc_ipfragfails()
+#define snmp_inc_ipfragcreates()
+#define snmp_inc_iproutingdiscards()
+#define snmp_insert_ipaddridx_tree(ni)
+#define snmp_delete_ipaddridx_tree(ni)
+#define snmp_insert_iprteidx_tree(dflt, ni)
+#define snmp_delete_iprteidx_tree(dflt, ni)
+
+/* ICMP */
+#define snmp_inc_icmpinmsgs()
+#define snmp_inc_icmpinerrors()
+#define snmp_inc_icmpindestunreachs()
+#define snmp_inc_icmpintimeexcds()
+#define snmp_inc_icmpinparmprobs()
+#define snmp_inc_icmpinsrcquenchs()
+#define snmp_inc_icmpinredirects()
+#define snmp_inc_icmpinechos()
+#define snmp_inc_icmpinechoreps()
+#define snmp_inc_icmpintimestamps()
+#define snmp_inc_icmpintimestampreps()
+#define snmp_inc_icmpinaddrmasks()
+#define snmp_inc_icmpinaddrmaskreps()
+#define snmp_inc_icmpoutmsgs()
+#define snmp_inc_icmpouterrors()
+#define snmp_inc_icmpoutdestunreachs()
+#define snmp_inc_icmpouttimeexcds()
+#define snmp_inc_icmpoutparmprobs()
+#define snmp_inc_icmpoutsrcquenchs()
+#define snmp_inc_icmpoutredirects()
+#define snmp_inc_icmpoutechos()
+#define snmp_inc_icmpoutechoreps()
+#define snmp_inc_icmpouttimestamps()
+#define snmp_inc_icmpouttimestampreps()
+#define snmp_inc_icmpoutaddrmasks()
+#define snmp_inc_icmpoutaddrmaskreps()
+/* TCP */
+#define snmp_inc_tcpactiveopens()
+#define snmp_inc_tcppassiveopens()
+#define snmp_inc_tcpattemptfails()
+#define snmp_inc_tcpestabresets()
+#define snmp_inc_tcpinsegs()
+#define snmp_inc_tcpoutsegs()
+#define snmp_inc_tcpretranssegs()
+#define snmp_inc_tcpinerrs()
+#define snmp_inc_tcpoutrsts()
+
+/* UDP */
+#define snmp_inc_udpindatagrams()
+#define snmp_inc_udpnoports()
+#define snmp_inc_udpinerrors()
+#define snmp_inc_udpoutdatagrams()
+#define snmp_insert_udpidx_tree(pcb)
+#define snmp_delete_udpidx_tree(pcb)
+
+/* SNMP */
+#define snmp_inc_snmpinpkts()
+#define snmp_inc_snmpoutpkts()
+#define snmp_inc_snmpinbadversions()
+#define snmp_inc_snmpinbadcommunitynames()
+#define snmp_inc_snmpinbadcommunityuses()
+#define snmp_inc_snmpinasnparseerrs()
+#define snmp_inc_snmpintoobigs()
+#define snmp_inc_snmpinnosuchnames()
+#define snmp_inc_snmpinbadvalues()
+#define snmp_inc_snmpinreadonlys()
+#define snmp_inc_snmpingenerrs()
+#define snmp_add_snmpintotalreqvars(value)
+#define snmp_add_snmpintotalsetvars(value)
+#define snmp_inc_snmpingetrequests()
+#define snmp_inc_snmpingetnexts()
+#define snmp_inc_snmpinsetrequests()
+#define snmp_inc_snmpingetresponses()
+#define snmp_inc_snmpintraps()
+#define snmp_inc_snmpouttoobigs()
+#define snmp_inc_snmpoutnosuchnames()
+#define snmp_inc_snmpoutbadvalues()
+#define snmp_inc_snmpoutgenerrs()
+#define snmp_inc_snmpoutgetrequests()
+#define snmp_inc_snmpoutgetnexts()
+#define snmp_inc_snmpoutsetrequests()
+#define snmp_inc_snmpoutgetresponses()
+#define snmp_inc_snmpouttraps()
+#define snmp_get_snmpgrpid_ptr(oid)
+#define snmp_set_snmpenableauthentraps(value)
+#define snmp_get_snmpenableauthentraps(value)
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_SNMP_H__ */
diff --git a/core/lwip/src/include/lwip/snmp_asn1.h b/core/lwip/src/include/lwip/snmp_asn1.h
new file mode 100644
index 00000000..605fa3f1
--- /dev/null
+++ b/core/lwip/src/include/lwip/snmp_asn1.h
@@ -0,0 +1,101 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef __LWIP_SNMP_ASN1_H__
+#define __LWIP_SNMP_ASN1_H__
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/snmp.h"
+
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */
+#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */
+#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */
+
+#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */
+#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */
+
+/* universal tags */
+#define SNMP_ASN1_INTEG 2
+#define SNMP_ASN1_OC_STR 4
+#define SNMP_ASN1_NUL 5
+#define SNMP_ASN1_OBJ_ID 6
+#define SNMP_ASN1_SEQ 16
+
+/* application specific (SNMP) tags */
+#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */
+#define SNMP_ASN1_COUNTER 1 /* u32_t */
+#define SNMP_ASN1_GAUGE 2 /* u32_t */
+#define SNMP_ASN1_TIMETICKS 3 /* u32_t */
+#define SNMP_ASN1_OPAQUE 4 /* octet string */
+
+/* context specific (SNMP) tags */
+#define SNMP_ASN1_PDU_GET_REQ 0
+#define SNMP_ASN1_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_PDU_GET_RESP 2
+#define SNMP_ASN1_PDU_SET_REQ 3
+#define SNMP_ASN1_PDU_TRAP 4
+
+err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type);
+err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length);
+err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid);
+err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed);
+err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type);
+err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length);
+err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident);
+err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* __LWIP_SNMP_ASN1_H__ */
diff --git a/core/lwip/src/include/lwip/snmp_msg.h b/core/lwip/src/include/lwip/snmp_msg.h
new file mode 100644
index 00000000..1183e3a9
--- /dev/null
+++ b/core/lwip/src/include/lwip/snmp_msg.h
@@ -0,0 +1,315 @@
+/**
+ * @file
+ * SNMP Agent message handling structures.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef __LWIP_SNMP_MSG_H__
+#define __LWIP_SNMP_MSG_H__
+
+#include "lwip/opt.h"
+#include "lwip/snmp.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP
+
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+#include "private_mib.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The listen port of the SNMP agent. Clients have to make their requests to
+ this port. Most standard clients won't work if you change this! */
+#ifndef SNMP_IN_PORT
+#define SNMP_IN_PORT 161
+#endif
+/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
+ work if you change this! */
+#ifndef SNMP_TRAP_PORT
+#define SNMP_TRAP_PORT 162
+#endif
+
+#define SNMP_ES_NOERROR 0
+#define SNMP_ES_TOOBIG 1
+#define SNMP_ES_NOSUCHNAME 2
+#define SNMP_ES_BADVALUE 3
+#define SNMP_ES_READONLY 4
+#define SNMP_ES_GENERROR 5
+
+#define SNMP_GENTRAP_COLDSTART 0
+#define SNMP_GENTRAP_WARMSTART 1
+#define SNMP_GENTRAP_AUTHFAIL 4
+#define SNMP_GENTRAP_ENTERPRISESPC 6
+
+struct snmp_varbind
+{
+ /* next pointer, NULL for last in list */
+ struct snmp_varbind *next;
+ /* previous pointer, NULL for first in list */
+ struct snmp_varbind *prev;
+
+ /* object identifier length (in s32_t) */
+ u8_t ident_len;
+ /* object identifier array */
+ s32_t *ident;
+
+ /* object value ASN1 type */
+ u8_t value_type;
+ /* object value length (in u8_t) */
+ u8_t value_len;
+ /* object value */
+ void *value;
+
+ /* encoding varbind seq length length */
+ u8_t seqlenlen;
+ /* encoding object identifier length length */
+ u8_t olenlen;
+ /* encoding object value length length */
+ u8_t vlenlen;
+ /* encoding varbind seq length */
+ u16_t seqlen;
+ /* encoding object identifier length */
+ u16_t olen;
+ /* encoding object value length */
+ u16_t vlen;
+};
+
+struct snmp_varbind_root
+{
+ struct snmp_varbind *head;
+ struct snmp_varbind *tail;
+ /* number of variable bindings in list */
+ u8_t count;
+ /* encoding varbind-list seq length length */
+ u8_t seqlenlen;
+ /* encoding varbind-list seq length */
+ u16_t seqlen;
+};
+
+/** output response message header length fields */
+struct snmp_resp_header_lengths
+{
+ /* encoding error-index length length */
+ u8_t erridxlenlen;
+ /* encoding error-status length length */
+ u8_t errstatlenlen;
+ /* encoding request id length length */
+ u8_t ridlenlen;
+ /* encoding pdu length length */
+ u8_t pdulenlen;
+ /* encoding community length length */
+ u8_t comlenlen;
+ /* encoding version length length */
+ u8_t verlenlen;
+ /* encoding sequence length length */
+ u8_t seqlenlen;
+
+ /* encoding error-index length */
+ u16_t erridxlen;
+ /* encoding error-status length */
+ u16_t errstatlen;
+ /* encoding request id length */
+ u16_t ridlen;
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding version length */
+ u16_t verlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+};
+
+/** output response message header length fields */
+struct snmp_trap_header_lengths
+{
+ /* encoding timestamp length length */
+ u8_t tslenlen;
+ /* encoding specific-trap length length */
+ u8_t strplenlen;
+ /* encoding generic-trap length length */
+ u8_t gtrplenlen;
+ /* encoding agent-addr length length */
+ u8_t aaddrlenlen;
+ /* encoding enterprise-id length length */
+ u8_t eidlenlen;
+ /* encoding pdu length length */
+ u8_t pdulenlen;
+ /* encoding community length length */
+ u8_t comlenlen;
+ /* encoding version length length */
+ u8_t verlenlen;
+ /* encoding sequence length length */
+ u8_t seqlenlen;
+
+ /* encoding timestamp length */
+ u16_t tslen;
+ /* encoding specific-trap length */
+ u16_t strplen;
+ /* encoding generic-trap length */
+ u16_t gtrplen;
+ /* encoding agent-addr length */
+ u16_t aaddrlen;
+ /* encoding enterprise-id length */
+ u16_t eidlen;
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding version length */
+ u16_t verlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+};
+
+/* Accepting new SNMP messages. */
+#define SNMP_MSG_EMPTY 0
+/* Search for matching object for variable binding. */
+#define SNMP_MSG_SEARCH_OBJ 1
+/* Perform SNMP operation on in-memory object.
+ Pass-through states, for symmetry only. */
+#define SNMP_MSG_INTERNAL_GET_OBJDEF 2
+#define SNMP_MSG_INTERNAL_GET_VALUE 3
+#define SNMP_MSG_INTERNAL_SET_TEST 4
+#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5
+#define SNMP_MSG_INTERNAL_SET_VALUE 6
+/* Perform SNMP operation on object located externally.
+ In theory this could be used for building a proxy agent.
+ Practical use is for an enterprise spc. app. gateway. */
+#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7
+#define SNMP_MSG_EXTERNAL_GET_VALUE 8
+#define SNMP_MSG_EXTERNAL_SET_TEST 9
+#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10
+#define SNMP_MSG_EXTERNAL_SET_VALUE 11
+
+#define SNMP_COMMUNITY_STR_LEN 64
+struct snmp_msg_pstat
+{
+ /* lwIP local port (161) binding */
+ struct udp_pcb *pcb;
+ /* source IP address */
+ ip_addr_t sip;
+ /* source UDP port */
+ u16_t sp;
+ /* request type */
+ u8_t rt;
+ /* request ID */
+ s32_t rid;
+ /* error status */
+ s32_t error_status;
+ /* error index */
+ s32_t error_index;
+ /* community name (zero terminated) */
+ u8_t community[SNMP_COMMUNITY_STR_LEN + 1];
+ /* community string length (exclusive zero term) */
+ u8_t com_strlen;
+ /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */
+ u8_t state;
+ /* saved arguments for MSG_EXTERNAL_x */
+ struct mib_external_node *ext_mib_node;
+ struct snmp_name_ptr ext_name_ptr;
+ struct obj_def ext_object_def;
+ struct snmp_obj_id ext_oid;
+ /* index into input variable binding list */
+ u8_t vb_idx;
+ /* ptr into input variable binding list */
+ struct snmp_varbind *vb_ptr;
+ /* list of variable bindings from input */
+ struct snmp_varbind_root invb;
+ /* list of variable bindings to output */
+ struct snmp_varbind_root outvb;
+ /* output response lengths used in ASN encoding */
+ struct snmp_resp_header_lengths rhl;
+};
+
+struct snmp_msg_trap
+{
+ /* lwIP local port (161) binding */
+ struct udp_pcb *pcb;
+ /* destination IP address in network order */
+ ip_addr_t dip;
+
+ /* source enterprise ID (sysObjectID) */
+ struct snmp_obj_id *enterprise;
+ /* source IP address, raw network order format */
+ u8_t sip_raw[4];
+ /* generic trap code */
+ u32_t gen_trap;
+ /* specific trap code */
+ u32_t spc_trap;
+ /* timestamp */
+ u32_t ts;
+ /* list of variable bindings to output */
+ struct snmp_varbind_root outvb;
+ /* output trap lengths used in ASN encoding */
+ struct snmp_trap_header_lengths thl;
+};
+
+/** Agent Version constant, 0 = v1 oddity */
+extern const s32_t snmp_version;
+/** Agent default "public" community string */
+extern const char snmp_publiccommunity[7];
+
+extern struct snmp_msg_trap trap_msg;
+
+/** Agent setup, start listening to port 161. */
+void snmp_init(void);
+void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
+void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst);
+
+/** Varbind-list functions. */
+struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len);
+void snmp_varbind_free(struct snmp_varbind *vb);
+void snmp_varbind_list_free(struct snmp_varbind_root *root);
+void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb);
+struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root);
+
+/** Handle an internal (recv) or external (private response) event. */
+void snmp_msg_event(u8_t request_id);
+err_t snmp_send_response(struct snmp_msg_pstat *m_stat);
+err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap);
+void snmp_coldstart_trap(void);
+void snmp_authfail_trap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* __LWIP_SNMP_MSG_H__ */
diff --git a/core/lwip/src/include/lwip/snmp_structs.h b/core/lwip/src/include/lwip/snmp_structs.h
new file mode 100644
index 00000000..0d3b46a9
--- /dev/null
+++ b/core/lwip/src/include/lwip/snmp_structs.h
@@ -0,0 +1,268 @@
+/**
+ * @file
+ * Generic MIB tree structures.
+ *
+ * @todo namespace prefixes
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef __LWIP_SNMP_STRUCTS_H__
+#define __LWIP_SNMP_STRUCTS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+#include "private_mib.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MIB object instance */
+#define MIB_OBJECT_NONE 0
+#define MIB_OBJECT_SCALAR 1
+#define MIB_OBJECT_TAB 2
+
+/* MIB access types */
+#define MIB_ACCESS_READ 1
+#define MIB_ACCESS_WRITE 2
+
+/* MIB object access */
+#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ
+#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE)
+#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE
+#define MIB_OBJECT_NOT_ACCESSIBLE 0
+
+/** object definition returned by (get_object_def)() */
+struct obj_def
+{
+ /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */
+ u8_t instance;
+ /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */
+ u8_t access;
+ /* ASN type for this object */
+ u8_t asn_type;
+ /* value length (host length) */
+ u16_t v_len;
+ /* length of instance part of supplied object identifier */
+ u8_t id_inst_len;
+ /* instance part of supplied object identifier */
+ s32_t *id_inst_ptr;
+};
+
+struct snmp_name_ptr
+{
+ u8_t ident_len;
+ s32_t *ident;
+};
+
+/** MIB const scalar (.0) node */
+#define MIB_NODE_SC 0x01
+/** MIB const array node */
+#define MIB_NODE_AR 0x02
+/** MIB array node (mem_malloced from RAM) */
+#define MIB_NODE_RA 0x03
+/** MIB list root node (mem_malloced from RAM) */
+#define MIB_NODE_LR 0x04
+/** MIB node for external objects */
+#define MIB_NODE_EX 0x05
+
+/** node "base class" layout, the mandatory fields for a node */
+struct mib_node
+{
+ /** returns struct obj_def for the given object identifier */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ /** returns object value for the given object identifier,
+ @note the caller must allocate at least len bytes for the value */
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ /** tests length and/or range BEFORE setting */
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ /** sets object value, only to be called when set_test() */
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+ /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */
+ u8_t node_type;
+ /* array or max list length */
+ u16_t maxlength;
+};
+
+/** derived node for scalars .0 index */
+typedef struct mib_node mib_scalar_node;
+
+/** derived node, points to a fixed size const array
+ of sub-identifiers plus a 'child' pointer */
+struct mib_array_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ const s32_t *objid;
+ struct mib_node* const *nptr;
+};
+
+/** derived node, points to a fixed size mem_malloced array
+ of sub-identifiers plus a 'child' pointer */
+struct mib_ram_array_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* aditional struct members */
+ s32_t *objid;
+ struct mib_node **nptr;
+};
+
+struct mib_list_node
+{
+ struct mib_list_node *prev;
+ struct mib_list_node *next;
+ s32_t objid;
+ struct mib_node *nptr;
+};
+
+/** derived node, points to a doubly linked list
+ of sub-identifiers plus a 'child' pointer */
+struct mib_list_rootnode
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ struct mib_list_node *head;
+ struct mib_list_node *tail;
+ /* counts list nodes in list */
+ u16_t count;
+};
+
+/** derived node, has access functions for mib object in external memory or device
+ using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */
+struct mib_external_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ /** points to an external (in memory) record of some sort of addressing
+ information, passed to and interpreted by the funtions below */
+ void* addr_inf;
+ /** tree levels under this node */
+ u8_t tree_levels;
+ /** number of objects at this level */
+ u16_t (*level_length)(void* addr_inf, u8_t level);
+ /** compares object sub identifier with external id
+ return zero when equal, nonzero when unequal */
+ s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id);
+ void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id);
+
+ /** async Questions */
+ void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident);
+ void (*get_value_q)(u8_t rid, struct obj_def *od);
+ void (*set_test_q)(u8_t rid, struct obj_def *od);
+ void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ /** async Answers */
+ void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ /** async Panic Close (agent returns error reply,
+ e.g. used for external transaction cleanup) */
+ void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident);
+ void (*get_value_pc)(u8_t rid, struct obj_def *od);
+ void (*set_test_pc)(u8_t rid, struct obj_def *od);
+ void (*set_value_pc)(u8_t rid, struct obj_def *od);
+};
+
+/** export MIB tree from mib2.c */
+extern const struct mib_array_node internet;
+
+/** dummy function pointers for non-leaf MIB nodes from mib2.c */
+void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+void noleafs_get_value(struct obj_def *od, u16_t len, void *value);
+u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value);
+void noleafs_set_value(struct obj_def *od, u16_t len, void *value);
+
+void snmp_oidtoip(s32_t *ident, ip_addr_t *ip);
+void snmp_iptooid(ip_addr_t *ip, s32_t *ident);
+void snmp_ifindextonetif(s32_t ifindex, struct netif **netif);
+void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx);
+
+struct mib_list_node* snmp_mib_ln_alloc(s32_t id);
+void snmp_mib_ln_free(struct mib_list_node *ln);
+struct mib_list_rootnode* snmp_mib_lrn_alloc(void);
+void snmp_mib_lrn_free(struct mib_list_rootnode *lrn);
+
+s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn);
+s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn);
+struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n);
+
+struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np);
+struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
+u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident);
+u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* __LWIP_SNMP_STRUCTS_H__ */
diff --git a/core/lwip/src/include/lwip/sockets.h b/core/lwip/src/include/lwip/sockets.h
new file mode 100644
index 00000000..3c8fed24
--- /dev/null
+++ b/core/lwip/src/include/lwip/sockets.h
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
+#ifndef __LWIP_SOCKETS_H__
+#define __LWIP_SOCKETS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/ip_addr.h"
+#include "lwip/inet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* members are in network byte order */
+struct sockaddr_in {
+ u8_t sin_len;
+ u8_t sin_family;
+ u16_t sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+struct sockaddr {
+ u8_t sa_len;
+ u8_t sa_family;
+ char sa_data[14];
+};
+
+#ifndef socklen_t
+# define socklen_t u32_t
+#endif
+
+/* Socket protocol types (TCP/UDP/RAW) */
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define SOCK_RAW 3
+
+/*
+ * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c)
+ */
+#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */
+#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
+#define SO_REUSEADDR 0x0004 /* Allow local address reuse */
+#define SO_KEEPALIVE 0x0008 /* keep connections alive */
+#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */
+#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */
+#define SO_LINGER 0x0080 /* linger on close if data present */
+#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */
+#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */
+
+#define SO_DONTLINGER ((int)(~SO_LINGER))
+
+/*
+ * Additional options, not kept in so_options.
+ */
+#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */
+#define SO_RCVBUF 0x1002 /* receive buffer size */
+#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */
+#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */
+#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */
+#define SO_RCVTIMEO 0x1006 /* receive timeout */
+#define SO_ERROR 0x1007 /* get error status and clear */
+#define SO_TYPE 0x1008 /* get socket type */
+#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */
+#define SO_NO_CHECK 0x100a /* don't create UDP checksum */
+
+
+/*
+ * Structure used for manipulating linger option.
+ */
+struct linger {
+ int l_onoff; /* option on/off */
+ int l_linger; /* linger time */
+};
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define SOL_SOCKET 0xfff /* options for socket level */
+
+
+#define AF_UNSPEC 0
+#define AF_INET 2
+#define PF_INET AF_INET
+#define PF_UNSPEC AF_UNSPEC
+
+#define IPPROTO_IP 0
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+#define IPPROTO_UDPLITE 136
+
+/* Flags we can use with send and recv. */
+#define MSG_PEEK 0x01 /* Peeks at an incoming message */
+#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */
+#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */
+#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
+#define MSG_MORE 0x10 /* Sender will send more */
+
+
+/*
+ * Options for level IPPROTO_IP
+ */
+#define IP_TOS 1
+#define IP_TTL 2
+
+#if LWIP_TCP
+/*
+ * Options for level IPPROTO_TCP
+ */
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */
+#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */
+#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */
+#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */
+#endif /* LWIP_TCP */
+
+#if LWIP_UDP && LWIP_UDPLITE
+/*
+ * Options for level IPPROTO_UDPLITE
+ */
+#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */
+#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+
+
+#if LWIP_IGMP
+/*
+ * Options and types for UDP multicast traffic handling
+ */
+#define IP_ADD_MEMBERSHIP 3
+#define IP_DROP_MEMBERSHIP 4
+#define IP_MULTICAST_TTL 5
+#define IP_MULTICAST_IF 6
+#define IP_MULTICAST_LOOP 7
+
+typedef struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+} ip_mreq;
+#endif /* LWIP_IGMP */
+
+/*
+ * The Type of Service provides an indication of the abstract
+ * parameters of the quality of service desired. These parameters are
+ * to be used to guide the selection of the actual service parameters
+ * when transmitting a datagram through a particular network. Several
+ * networks offer service precedence, which somehow treats high
+ * precedence traffic as more important than other traffic (generally
+ * by accepting only traffic above a certain precedence at time of high
+ * load). The major choice is a three way tradeoff between low-delay,
+ * high-reliability, and high-throughput.
+ * The use of the Delay, Throughput, and Reliability indications may
+ * increase the cost (in some sense) of the service. In many networks
+ * better performance for one of these parameters is coupled with worse
+ * performance on another. Except for very unusual cases at most two
+ * of these three indications should be set.
+ */
+#define IPTOS_TOS_MASK 0x1E
+#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IPTOS_LOWCOST 0x02
+#define IPTOS_MINCOST IPTOS_LOWCOST
+
+/*
+ * The Network Control precedence designation is intended to be used
+ * within a network only. The actual use and control of that
+ * designation is up to each network. The Internetwork Control
+ * designation is intended for use by gateway control originators only.
+ * If the actual use of these precedence designations is of concern to
+ * a particular network, it is the responsibility of that network to
+ * control the access to, and use of, those precedence designations.
+ */
+#define IPTOS_PREC_MASK 0xe0
+#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+
+/*
+ * Commands for ioctlsocket(), taken from the BSD file fcntl.h.
+ * lwip_ioctl only supports FIONREAD and FIONBIO, for now
+ *
+ * Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word. The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+#if !defined(FIONREAD) || !defined(FIONBIO)
+#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */
+#define IOC_VOID 0x20000000UL /* no parameters */
+#define IOC_OUT 0x40000000UL /* copy out parameters */
+#define IOC_IN 0x80000000UL /* copy in parameters */
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+ /* 0x20000000 distinguishes new &
+ old ioctl's */
+#define _IO(x,y) (IOC_VOID|((x)<<8)|(y))
+
+#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+
+#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+#endif /* !defined(FIONREAD) || !defined(FIONBIO) */
+
+#ifndef FIONREAD
+#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */
+#endif
+#ifndef FIONBIO
+#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */
+#endif
+
+/* Socket I/O Controls: unimplemented */
+#ifndef SIOCSHIWAT
+#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */
+#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */
+#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */
+#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */
+#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */
+#endif
+
+/* commands for fnctl */
+#ifndef F_GETFL
+#define F_GETFL 3
+#endif
+#ifndef F_SETFL
+#define F_SETFL 4
+#endif
+
+/* File status flags and file access modes for fnctl,
+ these are bits in an int. */
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 1 /* nonblocking I/O */
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */
+#endif
+
+#ifndef SHUT_RD
+ #define SHUT_RD 0
+ #define SHUT_WR 1
+ #define SHUT_RDWR 2
+#endif
+
+/* FD_SET used for lwip_select */
+#ifndef FD_SET
+ #undef FD_SETSIZE
+ /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */
+ #define FD_SETSIZE MEMP_NUM_NETCONN
+ #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7)))
+ #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7)))
+ #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7)))
+ #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p)))
+
+ typedef struct fd_set {
+ unsigned char fd_bits [(FD_SETSIZE+7)/8];
+ } fd_set;
+
+#endif /* FD_SET */
+
+/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
+ * by your system, set this to 0 and include <sys/time.h> in cc.h */
+#ifndef LWIP_TIMEVAL_PRIVATE
+#define LWIP_TIMEVAL_PRIVATE 1
+#endif
+
+#if LWIP_TIMEVAL_PRIVATE
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+#endif /* LWIP_TIMEVAL_PRIVATE */
+
+void lwip_socket_init(void);
+
+int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_shutdown(int s, int how);
+int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen);
+int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen);
+int lwip_close(int s);
+int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_listen(int s, int backlog);
+int lwip_recv(int s, void *mem, size_t len, int flags);
+int lwip_read(int s, void *mem, size_t len);
+int lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen);
+int lwip_send(int s, const void *dataptr, size_t size, int flags);
+int lwip_sendto(int s, const void *dataptr, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen);
+int lwip_socket(int domain, int type, int protocol);
+int lwip_write(int s, const void *dataptr, size_t size);
+int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout);
+int lwip_ioctl(int s, long cmd, void *argp);
+int lwip_fcntl(int s, int cmd, int val);
+
+#if LWIP_COMPAT_SOCKETS
+#define accept(a,b,c) lwip_accept(a,b,c)
+#define bind(a,b,c) lwip_bind(a,b,c)
+#define shutdown(a,b) lwip_shutdown(a,b)
+#define closesocket(s) lwip_close(s)
+#define connect(a,b,c) lwip_connect(a,b,c)
+#define getsockname(a,b,c) lwip_getsockname(a,b,c)
+#define getpeername(a,b,c) lwip_getpeername(a,b,c)
+#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e)
+#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e)
+#define listen(a,b) lwip_listen(a,b)
+#define recv(a,b,c,d) lwip_recv(a,b,c,d)
+#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)
+#define send(a,b,c,d) lwip_send(a,b,c,d)
+#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f)
+#define socket(a,b,c) lwip_socket(a,b,c)
+#define select(a,b,c,d,e) lwip_select(a,b,c,d,e)
+#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c)
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+#define read(a,b,c) lwip_read(a,b,c)
+#define write(a,b,c) lwip_write(a,b,c)
+#define close(s) lwip_close(s)
+#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
+
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET */
+
+#endif /* __LWIP_SOCKETS_H__ */
diff --git a/core/lwip/src/include/lwip/stats.h b/core/lwip/src/include/lwip/stats.h
new file mode 100644
index 00000000..015b7ce7
--- /dev/null
+++ b/core/lwip/src/include/lwip/stats.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_STATS_H__
+#define __LWIP_STATS_H__
+
+#include "lwip/opt.h"
+
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_STATS
+
+#ifndef LWIP_STATS_LARGE
+#define LWIP_STATS_LARGE 0
+#endif
+
+#if LWIP_STATS_LARGE
+#define STAT_COUNTER u32_t
+#define STAT_COUNTER_F U32_F
+#else
+#define STAT_COUNTER u16_t
+#define STAT_COUNTER_F U16_F
+#endif
+
+struct stats_proto {
+ STAT_COUNTER xmit; /* Transmitted packets. */
+ STAT_COUNTER recv; /* Received packets. */
+ STAT_COUNTER fw; /* Forwarded packets. */
+ STAT_COUNTER drop; /* Dropped packets. */
+ STAT_COUNTER chkerr; /* Checksum error. */
+ STAT_COUNTER lenerr; /* Invalid length error. */
+ STAT_COUNTER memerr; /* Out of memory error. */
+ STAT_COUNTER rterr; /* Routing error. */
+ STAT_COUNTER proterr; /* Protocol error. */
+ STAT_COUNTER opterr; /* Error in options. */
+ STAT_COUNTER err; /* Misc error. */
+ STAT_COUNTER cachehit;
+};
+
+struct stats_igmp {
+ STAT_COUNTER xmit; /* Transmitted packets. */
+ STAT_COUNTER recv; /* Received packets. */
+ STAT_COUNTER drop; /* Dropped packets. */
+ STAT_COUNTER chkerr; /* Checksum error. */
+ STAT_COUNTER lenerr; /* Invalid length error. */
+ STAT_COUNTER memerr; /* Out of memory error. */
+ STAT_COUNTER proterr; /* Protocol error. */
+ STAT_COUNTER rx_v1; /* Received v1 frames. */
+ STAT_COUNTER rx_group; /* Received group-specific queries. */
+ STAT_COUNTER rx_general; /* Received general queries. */
+ STAT_COUNTER rx_report; /* Received reports. */
+ STAT_COUNTER tx_join; /* Sent joins. */
+ STAT_COUNTER tx_leave; /* Sent leaves. */
+ STAT_COUNTER tx_report; /* Sent reports. */
+};
+
+struct stats_mem {
+#ifdef LWIP_DEBUG
+ const char *name;
+#endif /* LWIP_DEBUG */
+ mem_size_t avail;
+ mem_size_t used;
+ mem_size_t max;
+ STAT_COUNTER err;
+ STAT_COUNTER illegal;
+};
+
+struct stats_syselem {
+ STAT_COUNTER used;
+ STAT_COUNTER max;
+ STAT_COUNTER err;
+};
+
+struct stats_sys {
+ struct stats_syselem sem;
+ struct stats_syselem mutex;
+ struct stats_syselem mbox;
+};
+
+struct stats_ {
+#if LINK_STATS
+ struct stats_proto link;
+#endif
+#if ETHARP_STATS
+ struct stats_proto etharp;
+#endif
+#if IPFRAG_STATS
+ struct stats_proto ip_frag;
+#endif
+#if IP_STATS
+ struct stats_proto ip;
+#endif
+#if ICMP_STATS
+ struct stats_proto icmp;
+#endif
+#if IGMP_STATS
+ struct stats_igmp igmp;
+#endif
+#if UDP_STATS
+ struct stats_proto udp;
+#endif
+#if TCP_STATS
+ struct stats_proto tcp;
+#endif
+#if MEM_STATS
+ struct stats_mem mem;
+#endif
+#if MEMP_STATS
+ struct stats_mem memp[MEMP_MAX];
+#endif
+#if SYS_STATS
+ struct stats_sys sys;
+#endif
+};
+
+extern struct stats_ lwip_stats;
+
+void stats_init(void);
+
+#define STATS_INC(x) ++lwip_stats.x
+#define STATS_DEC(x) --lwip_stats.x
+#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \
+ if (lwip_stats.x.max < lwip_stats.x.used) { \
+ lwip_stats.x.max = lwip_stats.x.used; \
+ } \
+ } while(0)
+#else /* LWIP_STATS */
+#define stats_init()
+#define STATS_INC(x)
+#define STATS_DEC(x)
+#define STATS_INC_USED(x)
+#endif /* LWIP_STATS */
+
+#if TCP_STATS
+#define TCP_STATS_INC(x) STATS_INC(x)
+#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP")
+#else
+#define TCP_STATS_INC(x)
+#define TCP_STATS_DISPLAY()
+#endif
+
+#if UDP_STATS
+#define UDP_STATS_INC(x) STATS_INC(x)
+#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP")
+#else
+#define UDP_STATS_INC(x)
+#define UDP_STATS_DISPLAY()
+#endif
+
+#if ICMP_STATS
+#define ICMP_STATS_INC(x) STATS_INC(x)
+#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP")
+#else
+#define ICMP_STATS_INC(x)
+#define ICMP_STATS_DISPLAY()
+#endif
+
+#if IGMP_STATS
+#define IGMP_STATS_INC(x) STATS_INC(x)
+#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp)
+#else
+#define IGMP_STATS_INC(x)
+#define IGMP_STATS_DISPLAY()
+#endif
+
+#if IP_STATS
+#define IP_STATS_INC(x) STATS_INC(x)
+#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP")
+#else
+#define IP_STATS_INC(x)
+#define IP_STATS_DISPLAY()
+#endif
+
+#if IPFRAG_STATS
+#define IPFRAG_STATS_INC(x) STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG")
+#else
+#define IPFRAG_STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY()
+#endif
+
+#if ETHARP_STATS
+#define ETHARP_STATS_INC(x) STATS_INC(x)
+#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP")
+#else
+#define ETHARP_STATS_INC(x)
+#define ETHARP_STATS_DISPLAY()
+#endif
+
+#if LINK_STATS
+#define LINK_STATS_INC(x) STATS_INC(x)
+#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK")
+#else
+#define LINK_STATS_INC(x)
+#define LINK_STATS_DISPLAY()
+#endif
+
+#if MEM_STATS
+#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y
+#define MEM_STATS_INC(x) STATS_INC(mem.x)
+#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y)
+#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y
+#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP")
+#else
+#define MEM_STATS_AVAIL(x, y)
+#define MEM_STATS_INC(x)
+#define MEM_STATS_INC_USED(x, y)
+#define MEM_STATS_DEC_USED(x, y)
+#define MEM_STATS_DISPLAY()
+#endif
+
+#if MEMP_STATS
+#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y
+#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x)
+#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x)
+#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1)
+#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i)
+#else
+#define MEMP_STATS_AVAIL(x, i, y)
+#define MEMP_STATS_INC(x, i)
+#define MEMP_STATS_DEC(x, i)
+#define MEMP_STATS_INC_USED(x, i)
+#define MEMP_STATS_DISPLAY(i)
+#endif
+
+#if SYS_STATS
+#define SYS_STATS_INC(x) STATS_INC(sys.x)
+#define SYS_STATS_DEC(x) STATS_DEC(sys.x)
+#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1)
+#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys)
+#else
+#define SYS_STATS_INC(x)
+#define SYS_STATS_DEC(x)
+#define SYS_STATS_INC_USED(x)
+#define SYS_STATS_DISPLAY()
+#endif
+
+/* Display of statistics */
+#if LWIP_STATS_DISPLAY
+void stats_display(void);
+void stats_display_proto(struct stats_proto *proto, char *name);
+void stats_display_igmp(struct stats_igmp *igmp);
+void stats_display_mem(struct stats_mem *mem, char *name);
+void stats_display_memp(struct stats_mem *mem, int index);
+void stats_display_sys(struct stats_sys *sys);
+#else /* LWIP_STATS_DISPLAY */
+#define stats_display()
+#define stats_display_proto(proto, name)
+#define stats_display_igmp(igmp)
+#define stats_display_mem(mem, name)
+#define stats_display_memp(mem, index)
+#define stats_display_sys(sys)
+#endif /* LWIP_STATS_DISPLAY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_STATS_H__ */
diff --git a/core/lwip/src/include/lwip/sys.h b/core/lwip/src/include/lwip/sys.h
new file mode 100644
index 00000000..9f62c754
--- /dev/null
+++ b/core/lwip/src/include/lwip/sys.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_SYS_H__
+#define __LWIP_SYS_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if NO_SYS
+
+/* For a totally minimal and standalone system, we provide null
+ definitions of the sys_ functions. */
+typedef u8_t sys_sem_t;
+typedef u8_t sys_mutex_t;
+typedef u8_t sys_mbox_t;
+
+#define sys_sem_new(s, c) ERR_OK
+#define sys_sem_signal(s)
+#define sys_sem_wait(s)
+#define sys_arch_sem_wait(s,t)
+#define sys_sem_free(s)
+#define sys_mutex_new(mu) ERR_OK
+#define sys_mutex_lock(mu)
+#define sys_mutex_unlock(mu)
+#define sys_mutex_free(mu)
+#define sys_mbox_new(m, s) ERR_OK
+#define sys_mbox_fetch(m,d)
+#define sys_mbox_tryfetch(m,d)
+#define sys_mbox_post(m,d)
+#define sys_mbox_trypost(m,d)
+#define sys_mbox_free(m)
+
+#define sys_thread_new(n,t,a,s,p)
+
+#define sys_msleep(t)
+
+#else /* NO_SYS */
+
+/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */
+#define SYS_ARCH_TIMEOUT 0xffffffffUL
+
+/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate.
+ * For now we use the same magic value, but we allow this to change in future.
+ */
+#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
+
+#include "lwip/err.h"
+#include "arch/sys_arch.h"
+
+/** Function prototype for thread functions */
+typedef void (*lwip_thread_fn)(void *arg);
+
+/* Function prototypes for functions to be implemented by platform ports
+ (in sys_arch.c) */
+
+/* Mutex functions: */
+
+/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores
+ should be used instead */
+#if LWIP_COMPAT_MUTEX
+/* for old ports that don't have mutexes: define them to binary semaphores */
+#define sys_mutex_t sys_sem_t
+#define sys_mutex_new(mutex) sys_sem_new(mutex, 1)
+#define sys_mutex_lock(mutex) sys_sem_wait(mutex)
+#define sys_mutex_unlock(mutex) sys_sem_signal(mutex)
+#define sys_mutex_free(mutex) sys_sem_free(mutex)
+#define sys_mutex_valid(mutex) sys_sem_valid(mutex)
+#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex)
+
+#else /* LWIP_COMPAT_MUTEX */
+
+/** Create a new mutex
+ * @param mutex pointer to the mutex to create
+ * @return a new mutex */
+err_t sys_mutex_new(sys_mutex_t *mutex);
+/** Lock a mutex
+ * @param mutex the mutex to lock */
+void sys_mutex_lock(sys_mutex_t *mutex);
+/** Unlock a mutex
+ * @param mutex the mutex to unlock */
+void sys_mutex_unlock(sys_mutex_t *mutex);
+/** Delete a semaphore
+ * @param mutex the mutex to delete */
+void sys_mutex_free(sys_mutex_t *mutex);
+#ifndef sys_mutex_valid
+/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_mutex_valid(sys_mutex_t *mutex);
+#endif
+#ifndef sys_mutex_set_invalid
+/** Set a mutex invalid so that sys_mutex_valid returns 0 */
+void sys_mutex_set_invalid(sys_mutex_t *mutex);
+#endif
+#endif /* LWIP_COMPAT_MUTEX */
+
+/* Semaphore functions: */
+
+/** Create a new semaphore
+ * @param sem pointer to the semaphore to create
+ * @param count initial count of the semaphore
+ * @return ERR_OK if successful, another err_t otherwise */
+err_t sys_sem_new(sys_sem_t *sem, u8_t count);
+/** Signals a semaphore
+ * @param sem the semaphore to signal */
+void sys_sem_signal(sys_sem_t *sem);
+/** Wait for a semaphore for the specified timeout
+ * @param sem the semaphore to wait for
+ * @param timeout timeout in milliseconds to wait (0 = wait forever)
+ * @return time (in milliseconds) waited for the semaphore
+ * or SYS_ARCH_TIMEOUT on timeout */
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout);
+/** Delete a semaphore
+ * @param sem semaphore to delete */
+void sys_sem_free(sys_sem_t *sem);
+/** Wait for a semaphore - forever/no timeout */
+#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0)
+#ifndef sys_sem_valid
+/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_sem_valid(sys_sem_t *sem);
+#endif
+#ifndef sys_sem_set_invalid
+/** Set a semaphore invalid so that sys_sem_valid returns 0 */
+void sys_sem_set_invalid(sys_sem_t *sem);
+#endif
+
+/* Time functions. */
+#ifndef sys_msleep
+void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */
+#endif
+
+/* Mailbox functions. */
+
+/** Create a new mbox of specified size
+ * @param mbox pointer to the mbox to create
+ * @param size (miminum) number of messages in this mbox
+ * @return ERR_OK if successful, another err_t otherwise */
+err_t sys_mbox_new(sys_mbox_t *mbox, int size);
+/** Post a message to an mbox - may not fail
+ * -> blocks if full, only used from tasks not from ISR
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL) */
+void sys_mbox_post(sys_mbox_t *mbox, void *msg);
+/** Try to post a message to an mbox - may fail if full or ISR
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL) */
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg);
+/** Wait for a new message to arrive in the mbox
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @param timeout maximum time (in milliseconds) to wait for a message
+ * @return time (in milliseconds) waited for a message, may be 0 if not waited
+ or SYS_ARCH_TIMEOUT on timeout
+ * The returned time has to be accurate to prevent timer jitter! */
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout);
+/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */
+#ifndef sys_arch_mbox_tryfetch
+/** Wait for a new message to arrive in the mbox
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @param timeout maximum time (in milliseconds) to wait for a message
+ * @return 0 (milliseconds) if a message has been received
+ * or SYS_MBOX_EMPTY if the mailbox is empty */
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg);
+#endif
+/** For now, we map straight to sys_arch implementation. */
+#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg)
+/** Delete an mbox
+ * @param mbox mbox to delete */
+void sys_mbox_free(sys_mbox_t *mbox);
+#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0)
+#ifndef sys_mbox_valid
+/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_mbox_valid(sys_mbox_t *mbox);
+#endif
+#ifndef sys_mbox_set_invalid
+/** Set an mbox invalid so that sys_mbox_valid returns 0 */
+void sys_mbox_set_invalid(sys_mbox_t *mbox);
+#endif
+
+/** The only thread function:
+ * Creates a new thread
+ * @param name human-readable name for the thread (used for debugging purposes)
+ * @param thread thread-function
+ * @param arg parameter passed to 'thread'
+ * @param stacksize stack size in bytes for the new thread (may be ignored by ports)
+ * @param prio priority of the new thread (may be ignored by ports) */
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio);
+
+#endif /* NO_SYS */
+
+/* sys_init() must be called before anthing else. */
+void sys_init(void);
+
+#ifndef sys_jiffies
+/** Ticks/jiffies since power up. */
+u32_t sys_jiffies(void);
+#endif
+
+/** Returns the current time in milliseconds,
+ * may be the same as sys_jiffies or at least based on it. */
+u32_t sys_now(void);
+
+/* Critical Region Protection */
+/* These functions must be implemented in the sys_arch.c file.
+ In some implementations they can provide a more light-weight protection
+ mechanism than using semaphores. Otherwise semaphores can be used for
+ implementation */
+#ifndef SYS_ARCH_PROTECT
+/** SYS_LIGHTWEIGHT_PROT
+ * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
+ * for certain critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#if SYS_LIGHTWEIGHT_PROT
+
+/** SYS_ARCH_DECL_PROTECT
+ * declare a protection variable. This macro will default to defining a variable of
+ * type sys_prot_t. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h.
+ */
+#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev
+/** SYS_ARCH_PROTECT
+ * Perform a "fast" protect. This could be implemented by
+ * disabling interrupts for an embedded system or by using a semaphore or
+ * mutex. The implementation should allow calling SYS_ARCH_PROTECT when
+ * already protected. The old protection level is returned in the variable
+ * "lev". This macro will default to calling the sys_arch_protect() function
+ * which should be implemented in sys_arch.c. If a particular port needs a
+ * different implementation, then this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect()
+/** SYS_ARCH_UNPROTECT
+ * Perform a "fast" set of the protection level to "lev". This could be
+ * implemented by setting the interrupt level to "lev" within the MACRO or by
+ * using a semaphore or mutex. This macro will default to calling the
+ * sys_arch_unprotect() function which should be implemented in
+ * sys_arch.c. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev)
+sys_prot_t sys_arch_protect(void);
+void sys_arch_unprotect(sys_prot_t pval);
+
+#else
+
+#define SYS_ARCH_DECL_PROTECT(lev)
+#define SYS_ARCH_PROTECT(lev)
+#define SYS_ARCH_UNPROTECT(lev)
+
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+#endif /* SYS_ARCH_PROTECT */
+
+/*
+ * Macros to set/get and increase/decrease variables in a thread-safe way.
+ * Use these for accessing variable that are used from more than one thread.
+ */
+
+#ifndef SYS_ARCH_INC
+#define SYS_ARCH_INC(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var += val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_INC */
+
+#ifndef SYS_ARCH_DEC
+#define SYS_ARCH_DEC(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var -= val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_DEC */
+
+#ifndef SYS_ARCH_GET
+#define SYS_ARCH_GET(var, ret) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ ret = var; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_GET */
+
+#ifndef SYS_ARCH_SET
+#define SYS_ARCH_SET(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var = val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_SET */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_SYS_H__ */
diff --git a/core/lwip/src/include/lwip/tcp.h b/core/lwip/src/include/lwip/tcp.h
new file mode 100644
index 00000000..07dcd10e
--- /dev/null
+++ b/core/lwip/src/include/lwip/tcp.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCP_H__
+#define __LWIP_TCP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tcp_pcb;
+
+/** Function prototype for tcp accept callback functions. Called when a new
+ * connection can be accepted on a listening pcb.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param err An error code if there has been an error accepting.
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);
+
+/** Function prototype for tcp receive callback functions. Called when data has
+ * been received.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which received data
+ * @param p The received data (or NULL when the connection has been closed!)
+ * @param err An error code if there has been an error receiving
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
+ struct pbuf *p, err_t err);
+
+/** Function prototype for tcp sent callback functions. Called when sent data has
+ * been acknowledged by the remote side. Use it to free corresponding resources.
+ * This also means that the pcb has now space available to send new data.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb for which data has been acknowledged
+ * @param len The amount of bytes acknowledged
+ * @return ERR_OK: try to send some data by calling tcp_output
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb,
+ u16_t len);
+
+/** Function prototype for tcp poll callback functions. Called periodically as
+ * specified by @see tcp_poll.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb tcp pcb
+ * @return ERR_OK: try to send some data by calling tcp_output
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb);
+
+/** Function prototype for tcp error callback functions. Called when the pcb
+ * receives a RST or is unexpectedly closed for any other reason.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param err Error code to indicate why the pcb has been closed
+ * ERR_ABRT: aborted through tcp_abort or by a TCP timer
+ * ERR_RST: the connection was reset by the remote host
+ */
+typedef void (*tcp_err_fn)(void *arg, err_t err);
+
+/** Function prototype for tcp connected callback functions. Called when a pcb
+ * is connected to the remote side after initiating a connection attempt by
+ * calling tcp_connect().
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which is connected
+ * @param err An unused error code, always ERR_OK currently ;-) TODO!
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ *
+ * @note When a connection attempt fails, the error callback is currently called!
+ */
+typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err);
+
+enum tcp_state {
+ CLOSED = 0,
+ LISTEN = 1,
+ SYN_SENT = 2,
+ SYN_RCVD = 3,
+ ESTABLISHED = 4,
+ FIN_WAIT_1 = 5,
+ FIN_WAIT_2 = 6,
+ CLOSE_WAIT = 7,
+ CLOSING = 8,
+ LAST_ACK = 9,
+ TIME_WAIT = 10
+};
+
+#if LWIP_CALLBACK_API
+ /* Function to call when a listener has been connected.
+ * @param arg user-supplied argument (tcp_pcb.callback_arg)
+ * @param pcb a new tcp_pcb that now is connected
+ * @param err an error argument (TODO: that is current always ERR_OK?)
+ * @return ERR_OK: accept the new connection,
+ * any other err_t abortsthe new connection
+ */
+#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept;
+#else /* LWIP_CALLBACK_API */
+#define DEF_ACCEPT_CALLBACK
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * members common to struct tcp_pcb and struct tcp_listen_pcb
+ */
+#define TCP_PCB_COMMON(type) \
+ type *next; /* for the linked list */ \
+ enum tcp_state state; /* TCP state */ \
+ u8_t prio; \
+ void *callback_arg; \
+ /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \
+ DEF_ACCEPT_CALLBACK \
+ /* ports are in host byte order */ \
+ u16_t local_port
+
+
+/* the TCP protocol control block */
+struct tcp_pcb {
+/** common PCB members */
+ IP_PCB;
+/** protocol specific PCB members */
+ TCP_PCB_COMMON(struct tcp_pcb);
+
+ /* ports are in host byte order */
+ u16_t remote_port;
+
+ u8_t flags;
+#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */
+#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */
+#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */
+#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */
+#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */
+#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */
+#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */
+#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+
+ /* the rest of the fields are in host byte order
+ as we have to do some math with them */
+ /* receiver variables */
+ u32_t rcv_nxt; /* next seqno expected */
+ u16_t rcv_wnd; /* receiver window available */
+ u16_t rcv_ann_wnd; /* receiver window to announce */
+ u32_t rcv_ann_right_edge; /* announced right edge of window */
+
+ /* Timers */
+ u32_t tmr;
+ u8_t polltmr, pollinterval;
+
+ /* Retransmission timer. */
+ s16_t rtime;
+
+ u16_t mss; /* maximum segment size */
+
+ /* RTT (round trip time) estimation variables */
+ u32_t rttest; /* RTT estimate in 500ms ticks */
+ u32_t rtseq; /* sequence number being timed */
+ s16_t sa, sv; /* @todo document this */
+
+ s16_t rto; /* retransmission time-out */
+ u8_t nrtx; /* number of retransmissions */
+
+ /* fast retransmit/recovery */
+ u32_t lastack; /* Highest acknowledged seqno. */
+ u8_t dupacks;
+
+ /* congestion avoidance/control variables */
+ u16_t cwnd;
+ u16_t ssthresh;
+
+ /* sender variables */
+ u32_t snd_nxt; /* next new seqno to be sent */
+ u16_t snd_wnd; /* sender window */
+ u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
+ window update. */
+ u32_t snd_lbb; /* Sequence number of next byte to be buffered. */
+
+ u16_t acked;
+
+ u16_t snd_buf; /* Available buffer space for sending (in bytes). */
+#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3)
+ u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
+
+#if TCP_OVERSIZE
+ /* Extra bytes available at the end of the last pbuf in unsent. */
+ u16_t unsent_oversize;
+#endif /* TCP_OVERSIZE */
+
+ /* These are ordered by sequence number: */
+ struct tcp_seg *unsent; /* Unsent (queued) segments. */
+ struct tcp_seg *unacked; /* Sent but unacknowledged segments. */
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *ooseq; /* Received out of sequence segments. */
+#endif /* TCP_QUEUE_OOSEQ */
+
+ struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
+
+#if LWIP_CALLBACK_API
+ /* Function to be called when more send buffer space is available. */
+ tcp_sent_fn sent;
+ /* Function to be called when (in-sequence) data has arrived. */
+ tcp_recv_fn recv;
+ /* Function to be called when a connection has been set up. */
+ tcp_connected_fn connected;
+ /* Function which is called periodically. */
+ tcp_poll_fn poll;
+ /* Function to be called whenever a fatal error occurs. */
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+
+#if LWIP_TCP_TIMESTAMPS
+ u32_t ts_lastacksent;
+ u32_t ts_recent;
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+ /* idle time before KEEPALIVE is sent */
+ u32_t keep_idle;
+#if LWIP_TCP_KEEPALIVE
+ u32_t keep_intvl;
+ u32_t keep_cnt;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+ /* Persist timer counter */
+ u32_t persist_cnt;
+ /* Persist timer back-off */
+ u8_t persist_backoff;
+
+ /* KEEPALIVE counter */
+ u8_t keep_cnt_sent;
+};
+
+struct tcp_pcb_listen {
+/* Common members of all PCB types */
+ IP_PCB;
+/* Protocol specific PCB members */
+ TCP_PCB_COMMON(struct tcp_pcb_listen);
+
+#if TCP_LISTEN_BACKLOG
+ u8_t backlog;
+ u8_t accepts_pending;
+#endif /* TCP_LISTEN_BACKLOG */
+};
+
+#if LWIP_EVENT_API
+
+enum lwip_event {
+ LWIP_EVENT_ACCEPT,
+ LWIP_EVENT_SENT,
+ LWIP_EVENT_RECV,
+ LWIP_EVENT_CONNECTED,
+ LWIP_EVENT_POLL,
+ LWIP_EVENT_ERR
+};
+
+err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
+ enum lwip_event,
+ struct pbuf *p,
+ u16_t size,
+ err_t err);
+
+#endif /* LWIP_EVENT_API */
+
+/* Application program's interface: */
+struct tcp_pcb * tcp_new (void);
+
+void tcp_arg (struct tcp_pcb *pcb, void *arg);
+void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept);
+void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv);
+void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent);
+void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval);
+void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err);
+
+#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss)
+#define tcp_sndbuf(pcb) ((pcb)->snd_buf)
+#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen)
+#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY)
+#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY)
+#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0)
+
+#if TCP_LISTEN_BACKLOG
+#define tcp_accepted(pcb) do { \
+ LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \
+ (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0)
+#else /* TCP_LISTEN_BACKLOG */
+#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \
+ pcb->state == LISTEN)
+#endif /* TCP_LISTEN_BACKLOG */
+
+void tcp_recved (struct tcp_pcb *pcb, u16_t len);
+err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port, tcp_connected_fn connected);
+
+struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
+#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+
+void tcp_abort (struct tcp_pcb *pcb);
+err_t tcp_close (struct tcp_pcb *pcb);
+err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+ u8_t apiflags);
+
+void tcp_setprio (struct tcp_pcb *pcb, u8_t prio);
+
+#define TCP_PRIO_MIN 1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX 127
+
+err_t tcp_output (struct tcp_pcb *pcb);
+
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* __LWIP_TCP_H__ */
diff --git a/core/lwip/src/include/lwip/tcp_impl.h b/core/lwip/src/include/lwip/tcp_impl.h
new file mode 100644
index 00000000..b4feec0d
--- /dev/null
+++ b/core/lwip/src/include/lwip/tcp_impl.h
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCP_IMPL_H__
+#define __LWIP_TCP_IMPL_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Functions for interfacing with TCP: */
+
+/* Lower layer interface to TCP: */
+#define tcp_init() /* Compatibility define, no init needed. */
+void tcp_tmr (void); /* Must be called every
+ TCP_TMR_INTERVAL
+ ms. (Typically 250 ms). */
+/* It is also possible to call these two functions at the right
+ intervals (instead of calling tcp_tmr()). */
+void tcp_slowtmr (void);
+void tcp_fasttmr (void);
+
+
+/* Only used by IP to pass a TCP segment to TCP: */
+void tcp_input (struct pbuf *p, struct netif *inp);
+/* Used within the TCP code only: */
+struct tcp_pcb * tcp_alloc (u8_t prio);
+void tcp_abandon (struct tcp_pcb *pcb, int reset);
+err_t tcp_send_empty_ack(struct tcp_pcb *pcb);
+void tcp_rexmit (struct tcp_pcb *pcb);
+void tcp_rexmit_rto (struct tcp_pcb *pcb);
+void tcp_rexmit_fast (struct tcp_pcb *pcb);
+u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+
+/**
+ * This is the Nagle algorithm: try to combine user data to send as few TCP
+ * segments as possible. Only send if
+ * - no previously transmitted data on the connection remains unacknowledged or
+ * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
+ * - the only unsent segment is at least pcb->mss bytes long (or there is more
+ * than one unsent segment - with lwIP, this can happen although unsent->len < mss)
+ * - or if we are in fast-retransmit (TF_INFR)
+ */
+#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
+ ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
+ (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
+ ((tpcb)->unsent->len >= (tpcb)->mss))) \
+ ) ? 1 : 0)
+#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK)
+
+
+#define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0)
+#define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0)
+#define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0)
+#define TCP_SEQ_GEQ(a,b) ((s32_t)((a)-(b)) >= 0)
+/* is b<=a<=c? */
+#if 0 /* see bug #10548 */
+#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b))
+#endif
+#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
+#define TCP_FIN 0x01U
+#define TCP_SYN 0x02U
+#define TCP_RST 0x04U
+#define TCP_PSH 0x08U
+#define TCP_ACK 0x10U
+#define TCP_URG 0x20U
+#define TCP_ECE 0x40U
+#define TCP_CWR 0x80U
+
+#define TCP_FLAGS 0x3fU
+
+/* Length of the TCP header, excluding options. */
+#define TCP_HLEN 20
+
+#ifndef TCP_TMR_INTERVAL
+#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */
+#endif /* TCP_TMR_INTERVAL */
+
+#ifndef TCP_FAST_INTERVAL
+#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */
+#endif /* TCP_FAST_INTERVAL */
+
+#ifndef TCP_SLOW_INTERVAL
+#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */
+#endif /* TCP_SLOW_INTERVAL */
+
+#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
+#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
+
+#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */
+
+#ifndef TCP_MSL
+#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */
+#endif
+
+/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */
+#ifndef TCP_KEEPIDLE_DEFAULT
+#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */
+#endif
+
+#ifndef TCP_KEEPINTVL_DEFAULT
+#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */
+#endif
+
+#ifndef TCP_KEEPCNT_DEFAULT
+#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */
+#endif
+
+#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */
+
+/* Fields are (of course) in network byte order.
+ * Some fields are converted to host byte order in tcp_input().
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct tcp_hdr {
+ PACK_STRUCT_FIELD(u16_t src);
+ PACK_STRUCT_FIELD(u16_t dest);
+ PACK_STRUCT_FIELD(u32_t seqno);
+ PACK_STRUCT_FIELD(u32_t ackno);
+ PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
+ PACK_STRUCT_FIELD(u16_t wnd);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t urgp);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8)
+#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)
+#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)
+
+#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr))
+#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr))
+#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags))
+
+#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags))
+#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) )
+
+#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0))
+
+/** Flags used on input processing, not on pcb->flags
+*/
+#define TF_RESET (u8_t)0x08U /* Connection was reset. */
+#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */
+#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */
+
+
+#if LWIP_EVENT_API
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_ACCEPT, NULL, 0, err)
+#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_SENT, NULL, space, ERR_OK)
+#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_RECV, (p), 0, (err))
+#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_RECV, NULL, 0, ERR_OK)
+#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_CONNECTED, NULL, 0, (err))
+#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_POLL, NULL, 0, ERR_OK)
+#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \
+ LWIP_EVENT_ERR, NULL, 0, (err))
+
+#else /* LWIP_EVENT_API */
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret) \
+ do { \
+ if((pcb)->accept != NULL) \
+ (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \
+ else (ret) = ERR_ARG; \
+ } while (0)
+
+#define TCP_EVENT_SENT(pcb,space,ret) \
+ do { \
+ if((pcb)->sent != NULL) \
+ (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_RECV(pcb,p,err,ret) \
+ do { \
+ if((pcb)->recv != NULL) { \
+ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
+ } else { \
+ (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \
+ } \
+ } while (0)
+
+#define TCP_EVENT_CLOSED(pcb,ret) \
+ do { \
+ if(((pcb)->recv != NULL)) { \
+ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\
+ } else { \
+ (ret) = ERR_OK; \
+ } \
+ } while (0)
+
+#define TCP_EVENT_CONNECTED(pcb,err,ret) \
+ do { \
+ if((pcb)->connected != NULL) \
+ (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_POLL(pcb,ret) \
+ do { \
+ if((pcb)->poll != NULL) \
+ (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_ERR(errf,arg,err) \
+ do { \
+ if((errf) != NULL) \
+ (errf)((arg),(err)); \
+ } while (0)
+
+#endif /* LWIP_EVENT_API */
+
+/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */
+#if TCP_OVERSIZE && defined(LWIP_DEBUG)
+#define TCP_OVERSIZE_DBGCHECK 1
+#else
+#define TCP_OVERSIZE_DBGCHECK 0
+#endif
+
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
+#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP)
+
+/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
+struct tcp_seg {
+ struct tcp_seg *next; /* used when putting segements on a queue */
+ struct pbuf *p; /* buffer containing data + TCP header */
+ u16_t len; /* the TCP length of this segment */
+#if TCP_OVERSIZE_DBGCHECK
+ u16_t oversize_left; /* Extra bytes available at the end of the last
+ pbuf in unsent (used for asserting vs.
+ tcp_pcb.unsent_oversized only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum;
+ u8_t chksum_swapped;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ u8_t flags;
+#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */
+#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */
+#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is
+ checksummed into 'chksum' */
+ struct tcp_hdr *tcphdr; /* the TCP header */
+};
+
+#define LWIP_TCP_OPT_LENGTH(flags) \
+ (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \
+ (flags & TF_SEG_OPTS_TS ? 12 : 0)
+
+/** This returns a TCP header option for MSS in an u32_t */
+#define TCP_BUILD_MSS_OPTION(x) (x) = PP_HTONL(((u32_t)2 << 24) | \
+ ((u32_t)4 << 16) | \
+ (((u32_t)TCP_MSS / 256) << 8) | \
+ (TCP_MSS & 255))
+
+/* Global variables: */
+extern struct tcp_pcb *tcp_input_pcb;
+extern u32_t tcp_ticks;
+
+/* The TCP PCB lists. */
+union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */
+ struct tcp_pcb_listen *listen_pcbs;
+ struct tcp_pcb *pcbs;
+};
+extern struct tcp_pcb *tcp_bound_pcbs;
+extern union tcp_listen_pcbs_t tcp_listen_pcbs;
+extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a
+ state in which they accept or send
+ data. */
+extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */
+
+extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
+
+/* Axioms about the above lists:
+ 1) Every TCP PCB that is not CLOSED is in one of the lists.
+ 2) A PCB is only in one of the lists.
+ 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state.
+ 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state.
+*/
+/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB
+ with a PCB list or removes a PCB from a list, respectively. */
+#ifndef TCP_DEBUG_PCB_LISTS
+#define TCP_DEBUG_PCB_LISTS 0
+#endif
+#if TCP_DEBUG_PCB_LISTS
+#define TCP_REG(pcbs, npcb) do {\
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \
+ for(tcp_tmp_pcb = *(pcbs); \
+ tcp_tmp_pcb != NULL; \
+ tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \
+ } \
+ LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \
+ (npcb)->next = *(pcbs); \
+ LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \
+ *(pcbs) = (npcb); \
+ LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+ tcp_timer_needed(); \
+ } while(0)
+#define TCP_RMV(pcbs, npcb) do { \
+ LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \
+ if(*(pcbs) == (npcb)) { \
+ *(pcbs) = (*pcbs)->next; \
+ } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ if(tcp_tmp_pcb->next == (npcb)) { \
+ tcp_tmp_pcb->next = (npcb)->next; \
+ break; \
+ } \
+ } \
+ (npcb)->next = NULL; \
+ LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \
+ } while(0)
+
+#else /* LWIP_DEBUG */
+
+#define TCP_REG(pcbs, npcb) \
+ do { \
+ (npcb)->next = *pcbs; \
+ *(pcbs) = (npcb); \
+ tcp_timer_needed(); \
+ } while (0)
+
+#define TCP_RMV(pcbs, npcb) \
+ do { \
+ if(*(pcbs) == (npcb)) { \
+ (*(pcbs)) = (*pcbs)->next; \
+ } \
+ else { \
+ for(tcp_tmp_pcb = *pcbs; \
+ tcp_tmp_pcb != NULL; \
+ tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ if(tcp_tmp_pcb->next == (npcb)) { \
+ tcp_tmp_pcb->next = (npcb)->next; \
+ break; \
+ } \
+ } \
+ } \
+ (npcb)->next = NULL; \
+ } while(0)
+
+#endif /* LWIP_DEBUG */
+
+
+/* Internal functions: */
+struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
+void tcp_pcb_purge(struct tcp_pcb *pcb);
+void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb);
+
+void tcp_segs_free(struct tcp_seg *seg);
+void tcp_seg_free(struct tcp_seg *seg);
+struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
+
+#define tcp_ack(pcb) \
+ do { \
+ if((pcb)->flags & TF_ACK_DELAY) { \
+ (pcb)->flags &= ~TF_ACK_DELAY; \
+ (pcb)->flags |= TF_ACK_NOW; \
+ } \
+ else { \
+ (pcb)->flags |= TF_ACK_DELAY; \
+ } \
+ } while (0)
+
+#define tcp_ack_now(pcb) \
+ do { \
+ (pcb)->flags |= TF_ACK_NOW; \
+ } while (0)
+
+err_t tcp_send_fin(struct tcp_pcb *pcb);
+err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags);
+
+void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);
+
+void tcp_rst(u32_t seqno, u32_t ackno,
+ ip_addr_t *local_ip, ip_addr_t *remote_ip,
+ u16_t local_port, u16_t remote_port);
+
+u32_t tcp_next_iss(void);
+
+void tcp_keepalive(struct tcp_pcb *pcb);
+void tcp_zero_window_probe(struct tcp_pcb *pcb);
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+u16_t tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+#if LWIP_CALLBACK_API
+err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+void tcp_debug_print(struct tcp_hdr *tcphdr);
+void tcp_debug_print_flags(u8_t flags);
+void tcp_debug_print_state(enum tcp_state s);
+void tcp_debug_print_pcbs(void);
+s16_t tcp_pcbs_sane(void);
+#else
+# define tcp_debug_print(tcphdr)
+# define tcp_debug_print_flags(flags)
+# define tcp_debug_print_state(s)
+# define tcp_debug_print_pcbs()
+# define tcp_pcbs_sane() 1
+#endif /* TCP_DEBUG */
+
+/** External function (implemented in timers.c), called when TCP detects
+ * that a timer is needed (i.e. active- or time-wait-pcb found). */
+void tcp_timer_needed(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* __LWIP_TCP_H__ */
diff --git a/core/lwip/src/include/lwip/tcpip.h b/core/lwip/src/include/lwip/tcpip.h
new file mode 100644
index 00000000..995ba8ad
--- /dev/null
+++ b/core/lwip/src/include/lwip/tcpip.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCPIP_H__
+#define __LWIP_TCPIP_H__
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api_msg.h"
+#include "lwip/netifapi.h"
+#include "lwip/pbuf.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Define this to something that triggers a watchdog. This is called from
+ * tcpip_thread after processing a message. */
+#ifndef LWIP_TCPIP_THREAD_ALIVE
+#define LWIP_TCPIP_THREAD_ALIVE()
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+extern sys_mutex_t lock_tcpip_core;
+#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core)
+#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core)
+#define TCPIP_APIMSG(m) tcpip_apimsg_lock(m)
+#define TCPIP_APIMSG_ACK(m)
+#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m)
+#define TCPIP_NETIFAPI_ACK(m)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define LOCK_TCPIP_CORE()
+#define UNLOCK_TCPIP_CORE()
+#define TCPIP_APIMSG(m) tcpip_apimsg(m)
+#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed)
+#define TCPIP_NETIFAPI(m) tcpip_netifapi(m)
+#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** Function prototype for the init_done function passed to tcpip_init */
+typedef void (*tcpip_init_done_fn)(void *arg);
+/** Function prototype for functions passed to tcpip_callback() */
+typedef void (*tcpip_callback_fn)(void *ctx);
+
+void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
+
+#if LWIP_NETCONN
+err_t tcpip_apimsg(struct api_msg *apimsg);
+#if LWIP_TCPIP_CORE_LOCKING
+err_t tcpip_apimsg_lock(struct api_msg *apimsg);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETCONN */
+
+err_t tcpip_input(struct pbuf *p, struct netif *inp);
+
+#if LWIP_NETIF_API
+err_t tcpip_netifapi(struct netifapi_msg *netifapimsg);
+#if LWIP_TCPIP_CORE_LOCKING
+err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETIF_API */
+
+err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block);
+#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1)
+
+/* free pbufs or heap memory from another context without blocking */
+err_t pbuf_free_callback(struct pbuf *p);
+err_t mem_free_callback(void *m);
+
+#if LWIP_TCPIP_TIMEOUT
+err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg);
+err_t tcpip_untimeout(sys_timeout_handler h, void *arg);
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+enum tcpip_msg_type {
+#if LWIP_NETCONN
+ TCPIP_MSG_API,
+#endif /* LWIP_NETCONN */
+ TCPIP_MSG_INPKT,
+#if LWIP_NETIF_API
+ TCPIP_MSG_NETIFAPI,
+#endif /* LWIP_NETIF_API */
+#if LWIP_TCPIP_TIMEOUT
+ TCPIP_MSG_TIMEOUT,
+ TCPIP_MSG_UNTIMEOUT,
+#endif /* LWIP_TCPIP_TIMEOUT */
+ TCPIP_MSG_CALLBACK
+};
+
+struct tcpip_msg {
+ enum tcpip_msg_type type;
+ sys_sem_t *sem;
+ union {
+#if LWIP_NETCONN
+ struct api_msg *apimsg;
+#endif /* LWIP_NETCONN */
+#if LWIP_NETIF_API
+ struct netifapi_msg *netifapimsg;
+#endif /* LWIP_NETIF_API */
+ struct {
+ struct pbuf *p;
+ struct netif *netif;
+ } inp;
+ struct {
+ tcpip_callback_fn function;
+ void *ctx;
+ } cb;
+#if LWIP_TCPIP_TIMEOUT
+ struct {
+ u32_t msecs;
+ sys_timeout_handler h;
+ void *arg;
+ } tmo;
+#endif /* LWIP_TCPIP_TIMEOUT */
+ } msg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* __LWIP_TCPIP_H__ */
diff --git a/core/lwip/src/include/lwip/timers.h b/core/lwip/src/include/lwip/timers.h
new file mode 100644
index 00000000..fb92b4b4
--- /dev/null
+++ b/core/lwip/src/include/lwip/timers.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+#ifndef __LWIP_TIMERS_H__
+#define __LWIP_TIMERS_H__
+
+#include "lwip/opt.h"
+
+/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */
+#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS))
+
+#if LWIP_TIMERS
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LWIP_DEBUG_TIMERNAMES
+#ifdef LWIP_DEBUG
+#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUG_TIMERNAMES 0
+#endif /* LWIP_DEBUG*/
+#endif
+
+/** Function prototype for a timeout callback function. Register such a function
+ * using sys_timeout().
+ *
+ * @param arg Additional argument to pass to the function - set up by sys_timeout()
+ */
+typedef void (* sys_timeout_handler)(void *arg);
+
+struct sys_timeo {
+ struct sys_timeo *next;
+ u32_t time;
+ sys_timeout_handler h;
+ void *arg;
+#if LWIP_DEBUG_TIMERNAMES
+ const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+void sys_timeouts_init(void);
+
+#if LWIP_DEBUG_TIMERNAMES
+void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name);
+#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg);
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+void sys_untimeout(sys_timeout_handler handler, void *arg);
+#if NO_SYS
+void sys_check_timeouts(void);
+void sys_restart_timeouts(void);
+#else /* NO_SYS */
+void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg);
+#endif /* NO_SYS */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TIMERS */
+#endif /* __LWIP_TIMERS_H__ */
diff --git a/core/lwip/src/include/lwip/udp.h b/core/lwip/src/include/lwip/udp.h
new file mode 100644
index 00000000..c98c1b3e
--- /dev/null
+++ b/core/lwip/src/include/lwip/udp.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_UDP_H__
+#define __LWIP_UDP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UDP_HLEN 8
+
+/* Fields are (of course) in network byte order. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct udp_hdr {
+ PACK_STRUCT_FIELD(u16_t src);
+ PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */
+ PACK_STRUCT_FIELD(u16_t len);
+ PACK_STRUCT_FIELD(u16_t chksum);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define UDP_FLAGS_NOCHKSUM 0x01U
+#define UDP_FLAGS_UDPLITE 0x02U
+#define UDP_FLAGS_CONNECTED 0x04U
+#define UDP_FLAGS_MULTICAST_LOOP 0x08U
+
+struct udp_pcb;
+
+/** Function prototype for udp pcb receive callback functions
+ * addr and port are in same byte order as in the pcb
+ * The callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ *
+ * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf
+ * makes 'addr' invalid, too.
+ *
+ * @param arg user supplied argument (udp_pcb.recv_arg)
+ * @param pcb the udp_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @param port the remote port from which the packet was received
+ */
+typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr, u16_t port);
+
+
+struct udp_pcb {
+/* Common members of all PCB types */
+ IP_PCB;
+
+/* Protocol specific PCB members */
+
+ struct udp_pcb *next;
+
+ u8_t flags;
+ /** ports are in host byte order */
+ u16_t local_port, remote_port;
+
+#if LWIP_IGMP
+ /** outgoing network interface for multicast packets */
+ ip_addr_t multicast_ip;
+#endif /* LWIP_IGMP */
+
+#if LWIP_UDPLITE
+ /** used for UDP_LITE only */
+ u16_t chksum_len_rx, chksum_len_tx;
+#endif /* LWIP_UDPLITE */
+
+ /** receive callback function */
+ udp_recv_fn recv;
+ /** user-supplied argument for the recv callback */
+ void *recv_arg;
+};
+/* udp_pcbs export for exernal reference (e.g. SNMP agent) */
+extern struct udp_pcb *udp_pcbs;
+
+/* The following functions is the application layer interface to the
+ UDP code. */
+struct udp_pcb * udp_new (void);
+void udp_remove (struct udp_pcb *pcb);
+err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+void udp_disconnect (struct udp_pcb *pcb);
+void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv,
+ void *recv_arg);
+err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port,
+ struct netif *netif);
+err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port);
+err_t udp_send (struct udp_pcb *pcb, struct pbuf *p);
+
+#if LWIP_CHECKSUM_ON_COPY
+err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port,
+ struct netif *netif, u8_t have_chksum,
+ u16_t chksum);
+err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port,
+ u8_t have_chksum, u16_t chksum);
+err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#define udp_flags(pcb) ((pcb)->flags)
+#define udp_setflags(pcb, f) ((pcb)->flags = (f))
+
+/* The following functions are the lower layer interface to UDP. */
+void udp_input (struct pbuf *p, struct netif *inp);
+
+#define udp_init() /* Compatibility define, not init needed. */
+
+#if UDP_DEBUG
+void udp_debug_print(struct udp_hdr *udphdr);
+#else
+#define udp_debug_print(udphdr)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_UDP */
+
+#endif /* __LWIP_UDP_H__ */
diff --git a/core/lwip/src/include/lwipopts.h b/core/lwip/src/include/lwipopts.h
new file mode 100644
index 00000000..e0d39b89
--- /dev/null
+++ b/core/lwip/src/include/lwipopts.h
@@ -0,0 +1,73 @@
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+#include <byteswap.h>
+#include <netinet/in.h>
+
+#define SYS_LIGHTWEIGHT_PROT 1
+#define LWIP_NETIF_API 1
+#define LWIP_DNS 1
+#define LWIP_UDP 1
+#define LWIP_TCP 1
+#define LWIP_SO_RCVTIMEO 1
+#define LWIP_ICMP 1
+
+#define TCPIP_MBOX_SIZE 512
+#define TCPIP_THREAD_PRIO -10
+#define TCPIP_THREAD_STACKSIZE 32768
+
+#define DEFAULT_UDP_RECVMBOX_SIZE 16
+#define DEFAULT_TCP_RECVMBOX_SIZE 128
+#define DEFAULT_ACCEPTMBOX_SIZE 4
+
+#define LWIP_SOCKET 0
+
+#define MEM_LIBC_MALLOC 0
+#define MEMP_MEM_MALLOC 0
+
+#define MEMP_NUM_TCP_PCB 64
+#define MEMP_NUM_TCP_SEG 256
+#define MEMP_NUM_REASSDATA 32
+#define MEMP_NUM_SYS_TIMEOUT 8
+#define MEMP_NUM_NETCONN 64
+#define MEMP_NUM_TCPIP_MSG_API 64
+#define MEMP_NUM_TCPIP_MSG_INPKT 64
+#define MEMP_NUM_NETBUF 128
+#define PBUF_POOL_SIZE 256
+#define ARP_TABLE_SIZE 16
+#define IP_REASS_MAX_PBUFS 64
+#define IP_REASS_MAXAGE 10
+
+#define LWIP_NETIF_API 1
+
+#define LWIP_DNS 1
+#define DNS_TABLE_SIZE 16
+#define DNS_MAX_SERVERS 4
+#define TCP_MSS 1460
+#define TCP_WND 64000
+#define TCP_SND_BUF (4*TCP_MSS)
+#define LWIP_TCP_TIMESTAMPS 1
+
+/*
+ * IANA says to use dynamic port numbers above 49152, but some
+ * very high numbers are known to be (ab)used, too.
+ */
+#define TCP_LOCAL_PORT_RANGE_START 49152
+#define TCP_LOCAL_PORT_RANGE_END 57343
+#define UDP_LOCAL_PORT_RANGE_START 49152
+#define UDP_LOCAL_PORT_RANGE_END 57343
+
+#define ETHARP_TRUST_IP_MAC 0
+
+#define LWIP_STATS 1
+#define LWIP_STATS_DISPLAY 1
+
+#define LWIP_PLATFORM_BYTESWAP 1
+#define LWIP_PLATFORM_HTONS(x) bswap_16(x)
+#define LWIP_PLATFORM_HTONL(x) bswap_32(x)
+
+#define LWIP_PREFIX_BYTEORDER_FUNCS 0
+#define LWIP_COMPAT_MUTEX 1
+
+void undiarp_tmr(void);
+#endif /* __LWIPOPTS_H__ */
diff --git a/core/lwip/src/include/netif/etharp.h b/core/lwip/src/include/netif/etharp.h
new file mode 100644
index 00000000..691575fa
--- /dev/null
+++ b/core/lwip/src/include/netif/etharp.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __NETIF_ETHARP_H__
+#define __NETIF_ETHARP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ETHARP_HWADDR_LEN
+#define ETHARP_HWADDR_LEN 6
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct eth_addr {
+ PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** Ethernet header */
+struct eth_hdr {
+#if ETH_PAD_SIZE
+ PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
+#endif
+ PACK_STRUCT_FIELD(struct eth_addr dest);
+ PACK_STRUCT_FIELD(struct eth_addr src);
+ PACK_STRUCT_FIELD(u16_t type);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
+
+#if ETHARP_SUPPORT_VLAN
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** VLAN header inserted between ethernet header and payload
+ * if 'type' in ethernet header is ETHTYPE_VLAN.
+ * See IEEE802.Q */
+struct eth_vlan_hdr {
+ PACK_STRUCT_FIELD(u16_t prio_vid);
+ PACK_STRUCT_FIELD(u16_t tpid);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_VLAN_HDR 4
+#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF)
+
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** the ARP message, see RFC 826 ("Packet format") */
+struct etharp_hdr {
+ PACK_STRUCT_FIELD(u16_t hwtype);
+ PACK_STRUCT_FIELD(u16_t proto);
+ PACK_STRUCT_FIELD(u8_t hwlen);
+ PACK_STRUCT_FIELD(u8_t protolen);
+ PACK_STRUCT_FIELD(u16_t opcode);
+ PACK_STRUCT_FIELD(struct eth_addr shwaddr);
+ PACK_STRUCT_FIELD(struct ip_addr2 sipaddr);
+ PACK_STRUCT_FIELD(struct eth_addr dhwaddr);
+ PACK_STRUCT_FIELD(struct ip_addr2 dipaddr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETHARP_HDR 28
+#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR)
+
+/** 5 seconds period */
+#define ARP_TMR_INTERVAL 5000
+
+#define ETHTYPE_ARP 0x0806U
+#define ETHTYPE_IP 0x0800U
+#define ETHTYPE_VLAN 0x8100U
+#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */
+#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */
+
+/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables
+ * or known to be 32-bit aligned within the protocol header. */
+#ifndef ETHADDR32_COPY
+#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN)
+#endif
+
+/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local
+ * variables and known to be 16-bit aligned within the protocol header. */
+#ifndef ETHADDR16_COPY
+#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN)
+#endif
+
+#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** ARP message types (opcodes) */
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type)
+ * to a filter function that returns the correct netif when using multiple
+ * netifs on one hardware interface where the netif's low-level receive
+ * routine cannot decide for the correct netif (e.g. when mapping multiple
+ * IP addresses to one hardware interface).
+ */
+#ifndef LWIP_ARP_FILTER_NETIF
+#define LWIP_ARP_FILTER_NETIF 0
+#endif
+
+#if ARP_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+ * defined here to be accessed by memp.h
+ */
+struct etharp_q_entry {
+ struct etharp_q_entry *next;
+ struct pbuf *p;
+};
+#endif /* ARP_QUEUEING */
+
+#define etharp_init() /* Compatibility define, not init needed. */
+void etharp_tmr(void);
+s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
+ struct eth_addr **eth_ret, ip_addr_t **ip_ret);
+err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr);
+err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q);
+err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr);
+/** For Ethernet network interfaces, we might want to send "gratuitous ARP";
+ * this is an ARP packet sent by a node in order to spontaneously cause other
+ * nodes to update an entry in their ARP cache.
+ * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */
+#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr);
+err_t etharp_remove_static_entry(ip_addr_t *ipaddr);
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_AUTOIP
+err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
+ const u16_t opcode);
+#endif /* LWIP_AUTOIP */
+
+#endif /* LWIP_ARP */
+
+err_t ethernet_input(struct pbuf *p, struct netif *netif);
+
+#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0)
+
+extern const struct eth_addr ethbroadcast, ethzero;
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NETIF_ARP_H__ */
diff --git a/core/lwip/src/include/netif/ppp_oe.h b/core/lwip/src/include/netif/ppp_oe.h
new file mode 100644
index 00000000..e1cdfa51
--- /dev/null
+++ b/core/lwip/src/include/netif/ppp_oe.h
@@ -0,0 +1,190 @@
+/*****************************************************************************
+* ppp_oe.h - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef PPP_OE_H
+#define PPP_OE_H
+
+#include "lwip/opt.h"
+
+#if PPPOE_SUPPORT > 0
+
+#include "netif/etharp.h"
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoehdr {
+ PACK_STRUCT_FIELD(u8_t vertype);
+ PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FIELD(u16_t session);
+ PACK_STRUCT_FIELD(u16_t plen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoetag {
+ PACK_STRUCT_FIELD(u16_t tag);
+ PACK_STRUCT_FIELD(u16_t len);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+#define PPPOE_STATE_INITIAL 0
+#define PPPOE_STATE_PADI_SENT 1
+#define PPPOE_STATE_PADR_SENT 2
+#define PPPOE_STATE_SESSION 3
+#define PPPOE_STATE_CLOSING 4
+/* passive */
+#define PPPOE_STATE_PADO_SENT 1
+
+#define PPPOE_HEADERLEN sizeof(struct pppoehdr)
+#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */
+
+#define PPPOE_TAG_EOL 0x0000 /* end of list */
+#define PPPOE_TAG_SNAME 0x0101 /* service name */
+#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */
+#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */
+#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */
+#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */
+#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */
+#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */
+#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */
+#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */
+
+#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */
+#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */
+#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */
+#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */
+#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */
+
+#ifndef ETHERMTU
+#define ETHERMTU 1500
+#endif
+
+/* two byte PPP protocol discriminator, then IP data */
+#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2)
+
+#ifndef PPPOE_MAX_AC_COOKIE_LEN
+#define PPPOE_MAX_AC_COOKIE_LEN 64
+#endif
+
+struct pppoe_softc {
+ struct pppoe_softc *next;
+ struct netif *sc_ethif; /* ethernet interface we are using */
+ int sc_pd; /* ppp unit number */
+ void (*sc_linkStatusCB)(int pd, int up);
+
+ int sc_state; /* discovery phase or session connected */
+ struct eth_addr sc_dest; /* hardware address of concentrator */
+ u16_t sc_session; /* PPPoE session id */
+
+#ifdef PPPOE_TODO
+ char *sc_service_name; /* if != NULL: requested name of service */
+ char *sc_concentrator_name; /* if != NULL: requested concentrator id */
+#endif /* PPPOE_TODO */
+ u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */
+ size_t sc_ac_cookie_len; /* length of cookie data */
+#ifdef PPPOE_SERVER
+ u8_t *sc_hunique; /* content of host unique we must echo back */
+ size_t sc_hunique_len; /* length of host unique */
+#endif
+ int sc_padi_retried; /* number of PADI retries already done */
+ int sc_padr_retried; /* number of PADR retries already done */
+};
+
+
+#define pppoe_init() /* compatibility define, no initialization needed */
+
+err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr);
+err_t pppoe_destroy(struct netif *ifp);
+
+int pppoe_connect(struct pppoe_softc *sc);
+void pppoe_disconnect(struct pppoe_softc *sc);
+
+void pppoe_disc_input(struct netif *netif, struct pbuf *p);
+void pppoe_data_input(struct netif *netif, struct pbuf *p);
+
+err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
+
+/** used in ppp.c */
+#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN)
+
+#endif /* PPPOE_SUPPORT */
+
+#endif /* PPP_OE_H */
diff --git a/core/lwip/src/include/netif/slipif.h b/core/lwip/src/include/netif/slipif.h
new file mode 100644
index 00000000..ccd03c8a
--- /dev/null
+++ b/core/lwip/src/include/netif/slipif.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2001, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __NETIF_SLIPIF_H__
+#define __NETIF_SLIPIF_H__
+
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+err_t slipif_init(struct netif * netif);
+void slipif_poll(struct netif *netif);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/core/lwip/src/netif/FILES b/core/lwip/src/netif/FILES
new file mode 100644
index 00000000..099dbf3e
--- /dev/null
+++ b/core/lwip/src/netif/FILES
@@ -0,0 +1,29 @@
+This directory contains generic network interface device drivers that
+do not contain any hardware or architecture specific code. The files
+are:
+
+etharp.c
+ Implements the ARP (Address Resolution Protocol) over
+ Ethernet. The code in this file should be used together with
+ Ethernet device drivers. Note that this module has been
+ largely made Ethernet independent so you should be able to
+ adapt this for other link layers (such as Firewire).
+
+ethernetif.c
+ An example of how an Ethernet device driver could look. This
+ file can be used as a "skeleton" for developing new Ethernet
+ network device drivers. It uses the etharp.c ARP code.
+
+loopif.c
+ A "loopback" network interface driver. It requires configuration
+ through the define LWIP_LOOPIF_MULTITHREADING (see opt.h).
+
+slipif.c
+ A generic implementation of the SLIP (Serial Line IP)
+ protocol. It requires a sio (serial I/O) module to work.
+
+ppp/ Point-to-Point Protocol stack
+ The PPP stack has been ported from ucip (http://ucip.sourceforge.net).
+ It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although
+ compared to that, it has some modifications for embedded systems and
+ the source code has been reordered a bit. \ No newline at end of file
diff --git a/core/lwip/src/netif/etharp.c b/core/lwip/src/netif/etharp.c
new file mode 100644
index 00000000..b60dadd0
--- /dev/null
+++ b/core/lwip/src/netif/etharp.c
@@ -0,0 +1,1318 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include <string.h>
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** the time an ARP entry stays valid after its last update,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (240 * 5) seconds = 20 minutes.
+ */
+#define ARP_MAXAGE 240
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (2 * 5) seconds = 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 2
+
+#define HWTYPE_ETHERNET 1
+
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip_addr_t ipaddr;
+ struct eth_addr ethaddr;
+#if LWIP_SNMP
+ struct netif *netif;
+#endif /* LWIP_SNMP */
+ u8_t state;
+ u8_t ctime;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ u8_t static_entry;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#define ETHARP_FLAG_STATIC_ENTRY 4
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
+ *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags);
+
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+ #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ arp_table[i].static_entry = 0;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+#if LWIP_SNMP
+ arp_table[i].netif = NULL;
+#endif /* LWIP_SNMP */
+ ip_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (arp_table[i].static_entry == 0)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ free_entry(i);
+ }
+#if ARP_QUEUEING
+ /* still pending entry? (not expired) */
+ if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* resend an ARP query here? */
+ }
+#endif /* ARP_QUEUEING */
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags @see definition of ETHARP_FLAG_*
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+find_entry(ip_addr_t *ipaddr, u8_t flags)
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0, age_pending = 0, age_stable = 0;
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u8_t age_queue = 0;
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state == ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (arp_table[i].static_entry == 0)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ arp_table[i].static_entry = 0;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ return (err_t)i;
+}
+
+/**
+ * Send an IP packet on the network using netif->linkoutput
+ * The ethernet header is filled in before sending.
+ *
+ * @params netif the lwIP network interface on which to send the packet
+ * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
+ * @params src the source MAC address to be copied into the ethernet header
+ * @params dst the destination MAC address to be copied into the ethernet header
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+static err_t
+etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
+{
+ struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+ ETHADDR32_COPY(&ethhdr->dest, dst);
+ ETHADDR16_COPY(&ethhdr->src, src);
+ ethhdr->type = PP_HTONS(ETHTYPE_IP);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags @see definition of ETHARP_FLAG_*
+ *
+ * @return
+ * - ERR_OK Succesfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s8_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip_addr_isany(ipaddr) ||
+ ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = find_entry(ipaddr, flags);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].static_entry = 1;
+ }
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+
+#if LWIP_SNMP
+ /* record network interface */
+ arp_table[i].netif = netif;
+#endif /* LWIP_SNMP */
+ /* insert in SNMP ARP index tree */
+ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return @see return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+
+ netif = ip_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(ip_addr_t *ipaddr)
+{
+ s8_t i;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if ((arp_table[i].state != ETHARP_STATE_STABLE) ||
+ (arp_table[i].static_entry == 0)) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
+ struct eth_addr **eth_ret, ip_addr_t **ip_ret)
+{
+ s8_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+#if ETHARP_TRUST_IP_MAC
+/**
+ * Updates the ARP table using the given IP packet.
+ *
+ * Uses the incoming IP packet's source address to update the
+ * ARP cache for the local network. The function does not alter
+ * or free the packet. This function must be called before the
+ * packet p is passed to the IP layer.
+ *
+ * @param netif The lwIP network interface on which the IP packet pbuf arrived.
+ * @param p The IP packet that arrived on netif.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_ip_input(struct netif *netif, struct pbuf *p)
+{
+ struct eth_hdr *ethhdr;
+ struct ip_hdr *iphdr;
+ ip_addr_t iphdr_src;
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* Only insert an entry if the source IP address of the
+ incoming IP packet comes from a host on the local network. */
+ ethhdr = (struct eth_hdr *)p->payload;
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ ip_addr_copy(iphdr_src, iphdr->src);
+
+ /* source is not on the local network? */
+ if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
+ /* do nothing */
+ return;
+ }
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
+ /* update the source IP address in the cache, if present */
+ /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
+ * back soon (for example, if the destination IP address is ours. */
+ update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
+}
+#endif /* ETHARP_TRUST_IP_MAC */
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ * @param ethaddr Ethernet address of netif.
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
+{
+ struct etharp_hdr *hdr;
+ struct eth_hdr *ethhdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
+ since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
+ if (p->len < SIZEOF_ETHARP_PACKET) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
+ (s16_t)SIZEOF_ETHARP_PACKET));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETHARP_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip_addr_isany(&netif->ip_addr)) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possiby send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
+ /* Re-use pbuf to send ARP reply.
+ Since we are re-using an existing pbuf, we can't call etharp_raw since
+ that would allocate a new pbuf. */
+ hdr->opcode = htons(ARP_REPLY);
+
+ IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
+#endif /* LWIP_AUTOIP */
+
+ ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, &hdr->shwaddr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
+ ETHADDR16_COPY(&ethhdr->src, ethaddr);
+
+ /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
+ are already correct, we tested that before */
+
+ /* return ARP reply */
+ netif->linkoutput(netif, p);
+ /* we are not configured? */
+ } else if (ip_addr_isany(&netif->ip_addr)) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
+{
+ struct eth_addr *dest, mcastaddr;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+ }
+
+ /* assume unresolved Ethernet address */
+ dest = NULL;
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (struct eth_addr *)&ethbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = 0x01;
+ mcastaddr.addr[1] = 0x00;
+ mcastaddr.addr[2] = 0x5e;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ /* outside local network? */
+ if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
+ !ip_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
+ sizeof(struct eth_hdr));
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+ /* interface has default gateway? */
+ if (!ip_addr_isany(&netif->gw)) {
+ /* send to hardware address of default gateway IP address */
+ ipaddr = &(netif->gw);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t etharp_cached_entry = *(netif->addr_hint);
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
+ &arp_table[etharp_cached_entry].ethaddr);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+ /* queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, ipaddr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr) ||
+ ip_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i = find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state == ETHARP_STATE_STABLE)));
+
+ /* do we have a pending entry? or an implicit query request? */
+ if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state == ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_HINT(netif, i);
+ /* send the packet */
+ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ new_entry->next = 0;
+ new_entry->p = p;
+ if(arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ while (r->next != NULL) {
+ r = r->next;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+#if !LWIP_AUTOIP
+static
+#endif /* LWIP_AUTOIP */
+err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct eth_hdr *ethhdr;
+ struct etharp_hdr *hdr;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_PACKET));
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
+#endif /* LWIP_AUTOIP */
+ /* Write the ARP MAC-Addresses */
+ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+ ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+ /* Write the Ethernet MAC-Addresses */
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_addr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing. */
+ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+ IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETHARP_HWADDR_LEN;
+ hdr->protolen = sizeof(ip_addr_t);
+
+ ethhdr->type = PP_HTONS(ETHTYPE_ARP);
+ /* send ARP query */
+ result = netif->linkoutput(netif, p);
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, ip_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
+ ipaddr, ARP_REQUEST);
+}
+#endif /* LWIP_ARP */
+
+/**
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.
+ *
+ * @param p the recevied packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+ s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
+
+ if (p->len <= SIZEOF_ETH_HDR) {
+ /* a packet with only an ethernet header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
+ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
+ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
+ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
+ (unsigned)htons(ethhdr->type)));
+
+ type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+ if (type == PP_HTONS(ETHTYPE_VLAN)) {
+ struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
+ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+ /* a packet with only an ethernet/vlan header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */
+ if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+ /* silently ignore this packet: not for our VLAN */
+ pbuf_free(p);
+ return ERR_OK;
+ }
+#endif /* ETHARP_VLAN_CHECK */
+ type = vlan->tpid;
+ ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+ netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
+#endif /* LWIP_ARP_FILTER_NETIF*/
+
+ switch (type) {
+#if LWIP_ARP
+ /* IP packet? */
+ case PP_HTONS(ETHTYPE_IP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+#if ETHARP_TRUST_IP_MAC
+ /* update ARP table */
+ etharp_ip_input(netif, p);
+#endif /* ETHARP_TRUST_IP_MAC */
+ /* skip Ethernet header */
+ if(pbuf_header(p, -ip_hdr_offset)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ goto free_and_return;
+ } else {
+ /* pass to IP layer */
+ ip_input(p, netif);
+ }
+ break;
+
+ case PP_HTONS(ETHTYPE_ARP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* pass p to ARP module */
+ etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
+ break;
+#endif /* LWIP_ARP */
+#if PPPOE_SUPPORT
+ case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+ pppoe_disc_input(netif, p);
+ break;
+
+ case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+ pppoe_data_input(netif, p);
+ break;
+#endif /* PPPOE_SUPPORT */
+
+ default:
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* This means the pbuf is freed or consumed,
+ so the caller doesn't have to free it again */
+ return ERR_OK;
+
+free_and_return:
+ pbuf_free(p);
+ return ERR_OK;
+}
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/core/lwip/src/netif/ethernetif.c b/core/lwip/src/netif/ethernetif.c
new file mode 100644
index 00000000..a5b7d990
--- /dev/null
+++ b/core/lwip/src/netif/ethernetif.c
@@ -0,0 +1,318 @@
+/**
+ * @file
+ * Ethernet Interface Skeleton
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * This file is a skeleton for developing Ethernet network interface
+ * drivers for lwIP. Add code to the low_level functions and do a
+ * search-and-replace for the word "ethernetif" to replace it with
+ * something that better describes your network interface.
+ */
+
+#include "lwip/opt.h"
+
+#if 0 /* don't build, this is only a skeleton, see previous comment */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include <lwip/stats.h>
+#include <lwip/snmp.h>
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'e'
+#define IFNAME1 'n'
+
+/**
+ * Helper struct to hold private data used to operate your ethernet interface.
+ * Keeping the ethernet address of the MAC in this struct is not necessary
+ * as it is already kept in the struct netif.
+ * But this is only an example, anyway...
+ */
+struct ethernetif {
+ struct eth_addr *ethaddr;
+ /* Add whatever per-interface state that is needed here. */
+};
+
+/* Forward declarations. */
+static void ethernetif_input(struct netif *netif);
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ * for this ethernetif
+ */
+static void
+low_level_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+
+ /* set MAC hardware address length */
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+ /* set MAC hardware address */
+ netif->hwaddr[0] = ;
+ ...
+ netif->hwaddr[5] = ;
+
+ /* maximum transfer unit */
+ netif->mtu = 1500;
+
+ /* device capabilities */
+ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+
+ /* Do whatever else is needed to initialize interface. */
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ * an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ * strange results. You might consider waiting for space in the DMA queue
+ * to become availale since the stack doesn't retry to send a packet
+ * dropped because of memory failure (except for the TCP timers).
+ */
+
+static err_t
+low_level_output(struct netif *netif, struct pbuf *p)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *q;
+
+ initiate transfer();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ for(q = p; q != NULL; q = q->next) {
+ /* Send the data from the pbuf to the interface, one pbuf at a
+ time. The size of the data in each pbuf is kept in the ->len
+ variable. */
+ send data from(q->payload, q->len);
+ }
+
+ signal that packet should be sent();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.xmit);
+
+ return ERR_OK;
+}
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return a pbuf filled with the received packet (including MAC header)
+ * NULL on memory error
+ */
+static struct pbuf *
+low_level_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *p, *q;
+ u16_t len;
+
+ /* Obtain the size of the packet and put it into the "len"
+ variable. */
+ len = ;
+
+#if ETH_PAD_SIZE
+ len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+
+ if (p != NULL) {
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ /* We iterate over the pbuf chain until we have read the entire
+ * packet into the pbuf. */
+ for(q = p; q != NULL; q = q->next) {
+ /* Read enough bytes to fill this pbuf in the chain. The
+ * available data in the pbuf is given by the q->len
+ * variable.
+ * This does not necessarily have to be a memcpy, you can also preallocate
+ * pbufs for a DMA-enabled MAC and after receiving truncate it to the
+ * actually received size. In this case, ensure the tot_len member of the
+ * pbuf is the sum of the chained pbuf len members.
+ */
+ read data into(q->payload, q->len);
+ }
+ acknowledge that packet has been read();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.recv);
+ } else {
+ drop packet();
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ }
+
+ return p;
+}
+
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
+static void
+ethernetif_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+ struct eth_hdr *ethhdr;
+ struct pbuf *p;
+
+ ethernetif = netif->state;
+
+ /* move received packet into a new pbuf */
+ p = low_level_input(netif);
+ /* no packet could be read, silently ignore this */
+ if (p == NULL) return;
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = p->payload;
+
+ switch (htons(ethhdr->type)) {
+ /* IP or ARP packet? */
+ case ETHTYPE_IP:
+ case ETHTYPE_ARP:
+#if PPPOE_SUPPORT
+ /* PPPoE packet? */
+ case ETHTYPE_PPPOEDISC:
+ case ETHTYPE_PPPOE:
+#endif /* PPPOE_SUPPORT */
+ /* full packet send to tcpip_thread to process */
+ if (netif->input(p, netif)!=ERR_OK)
+ { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+ pbuf_free(p);
+ p = NULL;
+ }
+ break;
+
+ default:
+ pbuf_free(p);
+ p = NULL;
+ break;
+ }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ * any other err_t on error
+ */
+err_t
+ethernetif_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+ ethernetif = mem_malloc(sizeof(struct ethernetif));
+ if (ethernetif == NULL) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
+ return ERR_MEM;
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ /* Initialize interface hostname */
+ netif->hostname = "lwip";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ /*
+ * Initialize the snmp variables and counters inside the struct netif.
+ * The last argument should be replaced with your link speed, in units
+ * of bits per second.
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+
+ netif->state = ethernetif;
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+ /* We directly use etharp_output() here to save a function call.
+ * You can instead declare your own function an call etharp_output()
+ * from it if you have to do some checks before sending (e.g. if link
+ * is available...) */
+ netif->output = etharp_output;
+ netif->linkoutput = low_level_output;
+
+ ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
+
+ /* initialize the hardware */
+ low_level_init(netif);
+
+ return ERR_OK;
+}
+
+#endif /* 0 */
diff --git a/core/lwip/src/netif/ppp/auth.c b/core/lwip/src/netif/ppp/auth.c
new file mode 100644
index 00000000..c3c49d22
--- /dev/null
+++ b/core/lwip/src/netif/ppp/auth.c
@@ -0,0 +1,1334 @@
+/*****************************************************************************
+* auth.c - Network Authentication and Phase Control program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Ported from public pppd code.
+*****************************************************************************/
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+#include "lcp.h"
+#include "pap.h"
+#include "chap.h"
+#include "auth.h"
+#include "ipcp.h"
+
+#if CBCP_SUPPORT
+#include "cbcp.h"
+#endif /* CBCP_SUPPORT */
+
+#include "lwip/inet.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+#endif /* UNUSED */
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/* The name by which the peer authenticated itself to us. */
+static char peer_authname[MAXNAMELEN];
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* Set if we have run the /etc/ppp/auth-up script. */
+static int did_authup; /* @todo, we don't need this in lwip*/
+
+/* List of addresses which the peer may use. */
+static struct wordlist *addresses[NUM_PPP];
+
+#if 0 /* UNUSED */
+/* Wordlist giving addresses which the peer may use
+ without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+#endif /* UNUSED */
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+ s_down,
+ s_up
+};
+
+static enum script_state auth_state = s_down;
+static enum script_state auth_script_state = s_down;
+static pid_t auth_script_pid = 0;
+
+/*
+ * Option variables.
+ * lwip: some of these are present in the ppp_settings structure
+ */
+bool uselogin = 0; /* Use /etc/passwd for checking PAP */
+bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */
+bool usehostname = 0; /* Use hostname for our_name */
+bool auth_required = 0; /* Always require authentication from peer */
+bool allow_any_ip = 0; /* Allow peer to use any IP address */
+bool explicit_remote = 0; /* User specified explicit remote name */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+
+#endif /* UNUSED */
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+/* @todo, move this somewhere */
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char word[1];
+};
+
+
+extern char *crypt (const char *, const char *);
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase (int);
+static void check_idle (void *);
+static void connect_time_expired (void *);
+#if 0
+static int plogin (char *, char *, char **, int *);
+#endif
+static void plogout (void);
+static int null_login (int);
+static int get_pap_passwd (int, char *, char *);
+static int have_pap_secret (void);
+static int have_chap_secret (char *, char *, u32_t);
+static int ip_addr_check (u32_t, struct wordlist *);
+
+#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+static int scan_authfile (FILE *, char *, char *, char *,
+ struct wordlist **, struct wordlist **,
+ char *);
+static void free_wordlist (struct wordlist *);
+static void auth_script (char *);
+static void auth_script_done (void *);
+static void set_allowed_addrs (int unit, struct wordlist *addrs);
+static int some_ip_ok (struct wordlist *);
+static int setupapfile (char **);
+static int privgroup (char **);
+static int set_noauth_addr (char **);
+static void check_access (FILE *, char *);
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+ { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "refuse-pap", o_bool, &refuse_pap,
+ "Don't agree to auth to peer with PAP", 1 },
+ { "-pap", o_bool, &refuse_pap,
+ "Don't allow PAP authentication with peer", 1 },
+ { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "refuse-chap", o_bool, &refuse_chap,
+ "Don't agree to auth to peer with CHAP", 1 },
+ { "-chap", o_bool, &refuse_chap,
+ "Don't allow CHAP authentication with peer", 1 },
+ { "name", o_string, our_name,
+ "Set local name for authentication",
+ OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN },
+ { "user", o_string, user,
+ "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN },
+ { "usehostname", o_bool, &usehostname,
+ "Must use hostname for authentication", 1 },
+ { "remotename", o_string, remote_name,
+ "Set remote name for authentication", OPT_STATIC,
+ &explicit_remote, MAXNAMELEN },
+ { "auth", o_bool, &auth_required,
+ "Require authentication from peer", 1 },
+ { "noauth", o_bool, &auth_required,
+ "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip },
+ { "login", o_bool, &uselogin,
+ "Use system password database for PAP", 1 },
+ { "papcrypt", o_bool, &cryptpap,
+ "PAP passwords are encrypted", 1 },
+ { "+ua", o_special, (void *)setupapfile,
+ "Get PAP user and password from file" },
+ { "password", o_string, passwd,
+ "Password for authenticating us to the peer", OPT_STATIC,
+ NULL, MAXSECRETLEN },
+ { "privgroup", o_special, (void *)privgroup,
+ "Allow group members to use privileged options", OPT_PRIV },
+ { "allow-ip", o_special, (void *)set_noauth_addr,
+ "Set IP address(es) which can be used without authentication",
+ OPT_PRIV },
+ { NULL }
+};
+#endif /* UNUSED */
+#if 0 /* UNUSED */
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(char **argv)
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ seteuid(getuid());
+ ufile = fopen(*argv, "r");
+ seteuid(0);
+ if (ufile == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = 0;
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = 0;
+
+ return (1);
+}
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(char **argv)
+{
+ struct group *g;
+ int i;
+
+ g = getgrnam(*argv);
+ if (g == 0) {
+ option_error("group %s is unknown", *argv);
+ return 0;
+ }
+ for (i = 0; i < ngroups; ++i) {
+ if (groups[i] == g->gr_gid) {
+ privileged = 1;
+ break;
+ }
+ }
+ return 1;
+}
+#endif
+
+#if 0 /* UNUSED */
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(char **argv)
+{
+ char *addr = *argv;
+ int l = strlen(addr);
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1);
+ if (wp == NULL)
+ novm("allow-ip argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = noauth_addrs;
+ BCOPY(addr, wp->word, l);
+ noauth_addrs = wp;
+ return 1;
+}
+#endif /* UNUSED */
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+
+ AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit));
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(int unit)
+{
+ AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit));
+ if (lcp_phase[unit] == PHASE_DEAD) {
+ return;
+ }
+ if (logged_in) {
+ plogout();
+ }
+ lcp_phase[unit] = PHASE_DEAD;
+ AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n"));
+ pppLinkTerminated(unit);
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(int unit)
+{
+ int i;
+ struct protent *protp;
+
+ AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit));
+
+ if (did_authup) {
+ /* XXX Do link down processing. */
+ did_authup = 0;
+ }
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag) {
+ continue;
+ }
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) {
+ (*protp->lowerdown)(unit);
+ }
+ if (protp->protocol < 0xC000 && protp->close != NULL) {
+ (*protp->close)(unit, "LCP down");
+ }
+ }
+ num_np_open = 0; /* number of network protocols we have opened */
+ num_np_up = 0; /* Number of network protocols which have come up */
+
+ if (lcp_phase[unit] != PHASE_DEAD) {
+ lcp_phase[unit] = PHASE_TERMINATE;
+ }
+ pppLinkDown(unit);
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(int unit)
+{
+ int auth;
+ int i;
+ struct protent *protp;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+#if PAP_SUPPORT || CHAP_SUPPORT
+ lcp_options *ho = &lcp_hisoptions[unit];
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+ AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit));
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) {
+ (*protp->lowerup)(unit);
+ }
+ }
+ if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * treat it as though it authenticated with PAP using a username
+ * of "" and a password of "". If that's not OK, boot it out.
+ */
+ if (!wo->neg_upap || !null_login(unit)) {
+ AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n"));
+ lcp_close(unit, "peer refused to authenticate");
+ return;
+ }
+ }
+
+ lcp_phase[unit] = PHASE_AUTHENTICATE;
+ auth = 0;
+#if CHAP_SUPPORT
+ if (go->neg_chap) {
+ ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ }
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT && CHAP_SUPPORT
+ else
+#endif /* PAP_SUPPORT && CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (go->neg_upap) {
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (ho->neg_chap) {
+ ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ }
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT && CHAP_SUPPORT
+ else
+#endif /* PAP_SUPPORT && CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (ho->neg_upap) {
+ if (ppp_settings.passwd[0] == 0) {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) {
+ AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n"));
+ }
+ }
+ upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd);
+ auth |= PAP_WITHPEER;
+ }
+#endif /* PAP_SUPPORT */
+ auth_pending[unit] = auth;
+
+ if (!auth) {
+ network_phase(unit);
+ }
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(int unit)
+{
+ int i;
+ struct protent *protp;
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if ((go->neg_chap || go->neg_upap) && !did_authup) {
+ /* XXX Do setup for peer authentication. */
+ did_authup = 1;
+ }
+
+#if CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ lcp_phase[unit] = PHASE_CALLBACK;
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif /* CBCP_SUPPORT */
+
+ lcp_phase[unit] = PHASE_NETWORK;
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) {
+ (*protp->open)(unit);
+ if (protp->protocol != PPP_CCP) {
+ ++num_np_open;
+ }
+ }
+ }
+
+ if (num_np_open == 0) {
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+ }
+}
+/* @todo: add void start_networks(void) here (pppd 2.3.11) */
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(int unit, u16_t protocol)
+{
+ LWIP_UNUSED_ARG(protocol);
+
+ AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol));
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+}
+
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(int unit, u16_t protocol, char *name, int namelen)
+{
+ int pbit;
+
+ AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol));
+ switch (protocol) {
+ case PPP_CHAP:
+ pbit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ pbit = PAP_PEER;
+ break;
+ default:
+ AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > (int)sizeof(peer_authname) - 1) {
+ namelen = sizeof(peer_authname) - 1;
+ }
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = 0;
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~pbit) == 0) {
+ network_phase(unit);
+ }
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(int unit, u16_t protocol)
+{
+ int errCode = PPPERR_AUTHFAIL;
+
+ LWIP_UNUSED_ARG(protocol);
+
+ AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol));
+ if (passwd_from_file) {
+ BZERO(ppp_settings.passwd, MAXSECRETLEN);
+ }
+
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * He'll probably take the link down, and there's not much
+ * we can do except wait for that.
+ */
+ pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode);
+ lcp_close(unit, "Failed to authenticate ourselves to peer");
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(int unit, u16_t protocol)
+{
+ int pbit;
+
+ AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol));
+ switch (protocol) {
+ case PPP_CHAP:
+ pbit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file) {
+ BZERO(ppp_settings.passwd, MAXSECRETLEN);
+ }
+ pbit = PAP_WITHPEER;
+ break;
+ default:
+ AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
+ pbit = 0;
+ }
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~pbit) == 0) {
+ network_phase(unit);
+ }
+}
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto));
+ if (num_np_up == 0) {
+ AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit));
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ if (ppp_settings.idle_time_limit > 0) {
+ TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit);
+ }
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (ppp_settings.maxconnect > 0) {
+ TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect);
+ }
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto));
+ if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) {
+ UNTIMEOUT(check_idle, NULL);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto));
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(void *arg)
+{
+ struct ppp_idle idle;
+ u_short itime;
+
+ LWIP_UNUSED_ARG(arg);
+ if (!get_idle_time(0, &idle)) {
+ return;
+ }
+ itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
+ if (itime >= ppp_settings.idle_time_limit) {
+ /* link is idle: shut it down. */
+ AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n"));
+ lcp_close(0, "Link inactive");
+ } else {
+ TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ AUTHDEBUG(LOG_INFO, ("Connect time expired\n"));
+ lcp_close(0, "Connect time expired"); /* Close connection */
+}
+
+#if 0 /* UNUSED */
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options(void)
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u32_t remote;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) {
+ strcpy(ppp_settings.our_name, ppp_settings.hostname);
+ }
+
+ if (ppp_settings.user[0] == 0) {
+ strcpy(ppp_settings.user, ppp_settings.our_name);
+ }
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ can_auth = wo->neg_upap && have_pap_secret();
+ if (!can_auth && wo->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote);
+ }
+
+ if (ppp_settings.auth_required && !can_auth) {
+ ppp_panic("No auth secret");
+ }
+}
+#endif /* UNUSED */
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(int unit)
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u32_t remote;
+
+ AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit));
+ ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL));
+ ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/;
+
+ if (go->neg_upap && !have_pap_secret()) {
+ go->neg_upap = 0;
+ }
+ if (go->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) {
+ go->neg_chap = 0;
+ }
+ }
+}
+
+#if PAP_SUPPORT
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+u_char
+check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen)
+{
+#if 1 /* XXX Assume all entries OK. */
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(auser);
+ LWIP_UNUSED_ARG(userlen);
+ LWIP_UNUSED_ARG(apasswd);
+ LWIP_UNUSED_ARG(passwdlen);
+ LWIP_UNUSED_ARG(msglen);
+ *msg = (char *) 0;
+ return UPAP_AUTHACK; /* XXX Assume all entries OK. */
+#else
+ u_char ret = 0;
+ struct wordlist *addrs = NULL;
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static u_short attempts = 0;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ */
+ BCOPY(apasswd, passwd, passwdlen);
+ passwd[passwdlen] = '\0';
+ BCOPY(auser, user, userlen);
+ user[userlen] = '\0';
+ *msg = (char *) 0;
+
+ /* XXX Validate user name and password. */
+ ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */
+
+ if (ret == UPAP_AUTHNAK) {
+ if (*msg == (char *) 0) {
+ *msg = "Login incorrect";
+ }
+ *msglen = strlen(*msg);
+ /*
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user));
+ /*ppp_panic("Excess Bad Logins");*/
+ }
+ if (attempts > 3) {
+ /* @todo: this was sleep(), i.e. seconds, not milliseconds
+ * I don't think we really need this in lwIP - we would block tcpip_thread!
+ */
+ /*sys_msleep((attempts - 3) * 5);*/
+ }
+ if (addrs != NULL) {
+ free_wordlist(addrs);
+ }
+ } else {
+ attempts = 0; /* Reset count */
+ if (*msg == (char *) 0) {
+ *msg = "Login ok";
+ }
+ *msglen = strlen(*msg);
+ set_allowed_addrs(unit, addrs);
+ }
+
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+#endif
+}
+#endif /* PAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+
+/* lwip does not support PAM*/
+
+#endif /* USE_PAM */
+
+#endif /* UNUSED */
+
+
+#if 0 /* UNUSED */
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+static int
+plogin(char *user, char *passwd, char **msg, int *msglen)
+{
+
+ LWIP_UNUSED_ARG(user);
+ LWIP_UNUSED_ARG(passwd);
+ LWIP_UNUSED_ARG(msg);
+ LWIP_UNUSED_ARG(msglen);
+
+
+ /* The new lines are here align the file when
+ * compared against the pppd 2.3.11 code */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /* XXX Fail until we decide that we want to support logins. */
+ return (UPAP_AUTHNAK);
+}
+#endif
+
+
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout(void)
+{
+ logged_in = 0;
+}
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+ /* XXX Fail until we decide that we want to support logins. */
+ return 0;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ */
+static int
+get_pap_passwd(int unit, char *user, char *passwd)
+{
+ LWIP_UNUSED_ARG(unit);
+/* normally we would reject PAP if no password is provided,
+ but this causes problems with some providers (like CHT in Taiwan)
+ who incorrectly request PAP and expect a bogus/empty password, so
+ always provide a default user/passwd of "none"/"none"
+
+ @todo: This should be configured by the user, instead of being hardcoded here!
+*/
+ if(user) {
+ strcpy(user, "none");
+ }
+ if(passwd) {
+ strcpy(passwd, "none");
+ }
+ return 1;
+}
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(void)
+{
+ /* XXX Fail until we set up our passwords. */
+ return 0;
+}
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'. Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(char *client, char *server, u32_t remote)
+{
+ LWIP_UNUSED_ARG(client);
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(remote);
+
+ /* XXX Fail until we set up our passwords. */
+ return 0;
+}
+#if CHAP_SUPPORT
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs)
+{
+#if 1
+ int len;
+ struct wordlist *addrs;
+
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(save_addrs);
+
+ addrs = NULL;
+
+ if(!client || !client[0] || strcmp(client, ppp_settings.user)) {
+ return 0;
+ }
+
+ len = (int)strlen(ppp_settings.passwd);
+ if (len > MAXSECRETLEN) {
+ AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ len = MAXSECRETLEN;
+ }
+
+ BCOPY(ppp_settings.passwd, secret, len);
+ *secret_len = len;
+
+ return 1;
+#else
+ int ret = 0, len;
+ struct wordlist *addrs;
+ char secbuf[MAXWORDLEN];
+
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ /* XXX Find secret. */
+ if (ret < 0) {
+ return 0;
+ }
+
+ if (save_addrs) {
+ set_allowed_addrs(unit, addrs);
+ }
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ len = MAXSECRETLEN;
+ }
+
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+#endif
+}
+#endif /* CHAP_SUPPORT */
+
+
+#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ */
+static void
+set_allowed_addrs(int unit, struct wordlist *addrs)
+{
+ if (addresses[unit] != NULL) {
+ free_wordlist(addresses[unit]);
+ }
+ addresses[unit] = addrs;
+
+#if 0
+ /*
+ * If there's only one authorized address we might as well
+ * ask our peer for that one right away
+ */
+ if (addrs != NULL && addrs->next == NULL) {
+ char *p = addrs->word;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u32_t a;
+ struct hostent *hp;
+
+ if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) {
+ hp = gethostbyname(p);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u32_t *)hp->h_addr;
+ } else {
+ a = inet_addr(p);
+ }
+ if (a != (u32_t) -1) {
+ wo->hisaddr = a;
+ }
+ }
+ }
+#endif
+}
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(int unit, u32_t addr)
+{
+ return ip_addr_check(addr, addresses[unit]);
+}
+
+static int /* @todo: integrate this funtion into auth_ip_addr()*/
+ip_addr_check(u32_t addr, struct wordlist *addrs)
+{
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr)) {
+ return 0;
+ }
+
+ if (addrs == NULL) {
+ return !ppp_settings.auth_required; /* no addresses authorized */
+ }
+
+ /* XXX All other addresses allowed. */
+ return 1;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(u32_t addr)
+{
+ addr = ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(struct wordlist *addrs)
+{
+ for (; addrs != 0; addrs = addrs->next) {
+ if (addrs->word[0] == '-')
+ break;
+ if (addrs->word[0] != '!')
+ return 1; /* some IP address is allowed */
+ }
+ return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(FILE *f, char *filename)
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ warn("cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ warn("Warning - secret file %s has world and/or group access",
+ filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1
+ * if no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs. Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ */
+static int
+scan_authfile(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename)
+{
+ /* We do not (currently) need this in lwip */
+ return 0; /* dummy */
+}
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(struct wordlist *wp)
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+static void
+auth_script_done(void *arg)
+{
+ auth_script_pid = 0;
+ switch (auth_script_state) {
+ case s_up:
+ if (auth_state == s_down) {
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ break;
+ case s_down:
+ if (auth_state == s_up) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ break;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(char *script)
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[8];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ slprintf(struid, sizeof(struid), "%d", getuid());
+ user_name = struid;
+ }
+ slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+}
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/auth.h b/core/lwip/src/netif/ppp/auth.h
new file mode 100644
index 00000000..a8069ec4
--- /dev/null
+++ b/core/lwip/src/netif/ppp/auth.h
@@ -0,0 +1,111 @@
+/*****************************************************************************
+* auth.h - PPP Authentication and phase control header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original derived from BSD pppd.h.
+*****************************************************************************/
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef AUTH_H
+#define AUTH_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/* we are starting to use the link */
+void link_required (int);
+
+/* we are finished with the link */
+void link_terminated (int);
+
+/* the LCP layer has left the Opened state */
+void link_down (int);
+
+/* the link is up; authenticate now */
+void link_established (int);
+
+/* a network protocol has come up */
+void np_up (int, u16_t);
+
+/* a network protocol has gone down */
+void np_down (int, u16_t);
+
+/* a network protocol no longer needs link */
+void np_finished (int, u16_t);
+
+/* peer failed to authenticate itself */
+void auth_peer_fail (int, u16_t);
+
+/* peer successfully authenticated itself */
+void auth_peer_success (int, u16_t, char *, int);
+
+/* we failed to authenticate ourselves */
+void auth_withpeer_fail (int, u16_t);
+
+/* we successfully authenticated ourselves */
+void auth_withpeer_success (int, u16_t);
+
+/* check authentication options supplied */
+void auth_check_options (void);
+
+/* check what secrets we have */
+void auth_reset (int);
+
+/* Check peer-supplied username/password */
+u_char check_passwd (int, char *, int, char *, int, char **, int *);
+
+/* get "secret" for chap */
+int get_secret (int, char *, char *, char *, int *, int);
+
+/* check if IP address is authorized */
+int auth_ip_addr (int, u32_t);
+
+/* check if IP address is unreasonable */
+int bad_ip_adrs (u32_t);
+
+#endif /* AUTH_H */
diff --git a/core/lwip/src/netif/ppp/chap.c b/core/lwip/src/netif/ppp/chap.c
new file mode 100644
index 00000000..3a49ff8a
--- /dev/null
+++ b/core/lwip/src/netif/ppp/chap.c
@@ -0,0 +1,908 @@
+/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/
+/*****************************************************************************
+* chap.c - Network Challenge Handshake Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD chap.c.
+*****************************************************************************/
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "magic.h"
+#include "randm.h"
+#include "auth.h"
+#include "md5.h"
+#include "chap.h"
+#include "chpms.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+ { "chap-restart", o_int, &chap[0].timeouttime,
+ "Set timeout for CHAP" },
+ { "chap-max-challenge", o_int, &chap[0].max_transmits,
+ "Set max #xmits for challenge" },
+ { "chap-interval", o_int, &chap[0].chal_interval,
+ "Set interval for rechallenge" },
+#ifdef MSLANMAN
+ { "ms-lanman", o_bool, &ms_lanman,
+ "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+ { NULL }
+};
+#endif /* UNUSED */
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit (int);
+static void ChapLowerUp (int);
+static void ChapLowerDown (int);
+static void ChapInput (int, u_char *, int);
+static void ChapProtocolReject (int);
+#if PPP_ADDITIONAL_CALLBACKS
+static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *);
+#endif
+
+struct protent chap_protent = {
+ PPP_CHAP,
+ ChapInit,
+ ChapInput,
+ ChapProtocolReject,
+ ChapLowerUp,
+ ChapLowerDown,
+ NULL,
+ NULL,
+#if PPP_ADDITIONAL_CALLBACKS
+ ChapPrintPkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "CHAP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout (void *);
+static void ChapResponseTimeout (void *);
+static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int);
+static void ChapRechallenge (void *);
+static void ChapReceiveResponse (chap_state *, u_char *, int, int);
+static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len);
+static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len);
+static void ChapSendStatus (chap_state *, int);
+static void ChapSendChallenge (chap_state *);
+static void ChapSendResponse (chap_state *);
+static void ChapGenChallenge (chap_state *);
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(int unit, char *our_name, u_char digest)
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(int unit, char *our_name, u_char digest)
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE) {
+ return;
+ }
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n"));
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ return;
+ }
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN) {
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL) {
+ cstate->clientstate = CHAPCS_CLOSED;
+ } else if (cstate->clientstate == CHAPCS_PENDING) {
+ cstate->clientstate = CHAPCS_LISTEN;
+ }
+
+ if (cstate->serverstate == CHAPSS_INITIAL) {
+ cstate->serverstate = CHAPSS_CLOSED;
+ } else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE) {
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ } else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0) {
+ UNTIMEOUT(ChapRechallenge, cstate);
+ }
+ if (cstate->clientstate == CHAPCS_RESPONSE) {
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+ }
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED) {
+ auth_peer_fail(unit, PPP_CHAP);
+ }
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED) {
+ auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
+ }
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(int unit, u_char *inpacket, int packet_len)
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n"));
+ return;
+ }
+ if (len > packet_len) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n"));
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default: /* Need code reject? */
+ CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code));
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[256];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id));
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= (int)sizeof(rhostname)) {
+ len = sizeof(rhostname) - 1;
+ }
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n",
+ rhostname));
+
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) {
+ strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname));
+ rhostname[sizeof(rhostname) - 1] = 0;
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n",
+ rhostname));
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n",
+ rhostname));
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE) {
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+ }
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, (u_char*)secret, secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#if MSCHAP_SUPPORT
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type));
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len)
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[256];
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id));
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n",
+ cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id) {
+ return; /* doesn't match ID of last challenge */
+ }
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= (int)sizeof(rhostname)) {
+ len = sizeof(rhostname) - 1;
+ }
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n",
+ rhostname));
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
+ secret, &secret_len, 1)) {
+ CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n",
+ rhostname));
+ } else {
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE) {
+ break; /* it's not even the right length */
+ }
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, (u_char*)secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) {
+ code = CHAP_SUCCESS; /* they are the same! */
+ }
+ break;
+
+ default:
+ CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type));
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0) {
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ }
+ } else {
+ CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n"));
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(inp);
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id));
+
+ if (cstate->clientstate == CHAPCS_OPEN) {
+ /* presumably an answer to a duplicate response */
+ return;
+ }
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0) {
+ PRINTMSG(inp, len);
+ }
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(inp);
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id));
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0) {
+ PRINTMSG(inp, len);
+ }
+
+ CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n"));
+ auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(chap_state *cstate)
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = (int)strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id));
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(chap_state *cstate, int code)
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256]; /* @todo: this can be a char*, no strcpy needed */
+
+ if (code == CHAP_SUCCESS) {
+ strcpy(msg, "Welcome!");
+ } else {
+ strcpy(msg, "I don't like you. Go 'way.");
+ }
+ msglen = (int)strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code,
+ cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(chap_state *cstate)
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned)
+ ((((magic() >> 16) *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16)
+ + MIN_CHALLENGE_LENGTH);
+ LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff);
+ cstate->chal_len = (u_char)chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++ ) {
+ *ptr++ = (char) (magic() & 0xff);
+ }
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(chap_state *cstate)
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = (int)strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static int
+ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN) {
+ return 0;
+ }
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen) {
+ return 0;
+ }
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) {
+ printer(arg, " %s", ChapCodenames[code-1]);
+ } else {
+ printer(arg, " code=0x%x", code);
+ }
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1) {
+ break;
+ }
+ clen = p[0];
+ if (len < clen + 1) {
+ break;
+ }
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = %.*Z", nlen, p);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " %.*Z", len, p);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* CHAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/chap.h b/core/lwip/src/netif/ppp/chap.h
new file mode 100644
index 00000000..fedcab8d
--- /dev/null
+++ b/core/lwip/src/netif/ppp/chap.h
@@ -0,0 +1,150 @@
+/*****************************************************************************
+* chap.h - Network Challenge Handshake Authentication Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original built from BSD network code.
+******************************************************************************/
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.h,v 1.6 2010/01/24 13:19:34 goldsimon Exp $
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 32
+#define MAX_CHALLENGE_LENGTH 64
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ char *resp_name; /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer (int, char *, u_char);
+void ChapAuthPeer (int, char *, u_char);
+
+extern struct protent chap_protent;
+
+#endif /* CHAP_H */
diff --git a/core/lwip/src/netif/ppp/chpms.c b/core/lwip/src/netif/ppp/chpms.c
new file mode 100644
index 00000000..83acefce
--- /dev/null
+++ b/core/lwip/src/netif/ppp/chpms.c
@@ -0,0 +1,396 @@
+/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/
+/*** The original PPPD code is written in a way to require either the UNIX DES
+ encryption functions encrypt(3) and setkey(3) or the DES library libdes.
+ Since both is not included in lwIP, MSCHAP currently does not work! */
+/*****************************************************************************
+* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD chap_ms.c.
+*****************************************************************************/
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#define USE_CRYPT
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "md4.h"
+#ifndef USE_CRYPT
+#include "des.h"
+#endif
+#include "chap.h"
+#include "chpms.h"
+
+#include <string.h>
+
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+
+/************************/
+/*** LOCAL DATA TYPES ***/
+/************************/
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+
+/***********************************/
+/*** LOCAL FUNCTION DECLARATIONS ***/
+/***********************************/
+
+/* XXX Don't know what to do with these. */
+extern void setkey(const char *);
+extern void encrypt(char *, int);
+
+static void DesEncrypt (u_char *, u_char *, u_char *);
+static void MakeKey (u_char *, u_char *);
+
+#ifdef USE_CRYPT
+static void Expand (u_char *, u_char *);
+static void Collapse (u_char *, u_char *);
+#endif
+
+static void ChallengeResponse(
+ u_char *challenge, /* IN 8 octets */
+ u_char *pwHash, /* IN 16 octets */
+ u_char *response /* OUT 24 octets */
+);
+static void ChapMS_NT(
+ char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response
+);
+static u_char Get7Bits(
+ u_char *input,
+ int startBit
+);
+
+static void
+ChallengeResponse( u_char *challenge, /* IN 8 octets */
+ u_char *pwHash, /* IN 16 octets */
+ u_char *response /* OUT 24 octets */)
+{
+ u_char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, 16);
+
+#if 0
+ log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt( u_char *clear, /* IN 8 octets */
+ u_char *key, /* IN 7 octets */
+ u_char *cipher /* OUT 8 octets */)
+{
+ u_char des_key[8];
+ u_char crypt_key[66];
+ u_char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey((char*)crypt_key);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ Expand(clear, des_input);
+ encrypt((char*)des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt( u_char *clear, /* IN 8 octets */
+ u_char *key, /* IN 7 octets */
+ u_char *cipher /* OUT 8 octets */)
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char
+Get7Bits( u_char *input, int startBit)
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void
+Expand(u_char *in, u_char *out)
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--) {
+ *out++ = (c >> j) & 01;
+ }
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void
+Collapse(u_char *in, u_char *out)
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++) {
+ c |= *in << j;
+ }
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void
+MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */
+ u_char *des_key /* OUT 64 bit DES key with parity bits added */)
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n",
+ key[0], key[1], key[2], key[3], key[4], key[5], key[6]));
+ CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7]));
+#endif
+}
+
+static void
+ChapMS_NT( char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response)
+{
+ int i;
+ MDstruct md4Context;
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ static int low_byte_first = -1;
+
+ LWIP_UNUSED_ARG(rchallenge_len);
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++) {
+ unicodePassword[i * 2] = (u_char)secret[i];
+ }
+ MDbegin(&md4Context);
+ MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */
+
+ if (low_byte_first == -1) {
+ low_byte_first = (PP_HTONS((unsigned short int)1) != 1);
+ }
+ if (low_byte_first == 0) {
+ /* @todo: arg type - u_long* or u_int* ? */
+ MDreverse((unsigned int*)&md4Context); /* sfb 961105 */
+ }
+
+ MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */
+
+ ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan( char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response)
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[16];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++) {
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+ }
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len)
+{
+ MS_ChapResponse response;
+#ifdef MSLANMAN
+ extern int ms_lanman;
+#endif
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+#endif /* MSCHAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/chpms.h b/core/lwip/src/netif/ppp/chpms.h
new file mode 100644
index 00000000..df070fb3
--- /dev/null
+++ b/core/lwip/src/netif/ppp/chpms.h
@@ -0,0 +1,64 @@
+/*****************************************************************************
+* chpms.h - Network Microsoft Challenge Handshake Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-01-30 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original built from BSD network code.
+******************************************************************************/
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $
+ */
+
+#ifndef CHPMS_H
+#define CHPMS_H
+
+#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
+
+void ChapMS (chap_state *, char *, int, char *, int);
+
+#endif /* CHPMS_H */
diff --git a/core/lwip/src/netif/ppp/fsm.c b/core/lwip/src/netif/ppp/fsm.c
new file mode 100644
index 00000000..2e73c5af
--- /dev/null
+++ b/core/lwip/src/netif/ppp/fsm.c
@@ -0,0 +1,890 @@
+/*****************************************************************************
+* fsm.c - Network Control Protocol Finite State Machine program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD fsm.c.
+*****************************************************************************/
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+
+#include <string.h>
+
+#if PPP_DEBUG
+static const char *ppperr_strerr[] = {
+ "LS_INITIAL", /* LS_INITIAL 0 */
+ "LS_STARTING", /* LS_STARTING 1 */
+ "LS_CLOSED", /* LS_CLOSED 2 */
+ "LS_STOPPED", /* LS_STOPPED 3 */
+ "LS_CLOSING", /* LS_CLOSING 4 */
+ "LS_STOPPING", /* LS_STOPPING 5 */
+ "LS_REQSENT", /* LS_REQSENT 6 */
+ "LS_ACKRCVD", /* LS_ACKRCVD 7 */
+ "LS_ACKSENT", /* LS_ACKSENT 8 */
+ "LS_OPENED" /* LS_OPENED 9 */
+};
+#endif /* PPP_DEBUG */
+
+static void fsm_timeout (void *);
+static void fsm_rconfreq (fsm *, u_char, u_char *, int);
+static void fsm_rconfack (fsm *, int, u_char *, int);
+static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
+static void fsm_rtermreq (fsm *, int, u_char *, int);
+static void fsm_rtermack (fsm *);
+static void fsm_rcoderej (fsm *, u_char *, int);
+static void fsm_sconfreq (fsm *, int);
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(fsm *f)
+{
+ f->state = LS_INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = FSM_DEFTIMEOUT;
+ f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
+ f->maxtermtransmits = FSM_DEFMAXTERMREQS;
+ f->maxnakloops = FSM_DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_INITIAL:
+ f->state = LS_CLOSED;
+ break;
+
+ case LS_STARTING:
+ if( f->flags & OPT_SILENT ) {
+ f->state = LS_STOPPED;
+ } else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_CLOSED:
+ f->state = LS_INITIAL;
+ break;
+
+ case LS_STOPPED:
+ f->state = LS_STARTING;
+ if( f->callbacks->starting ) {
+ (*f->callbacks->starting)(f);
+ }
+ break;
+
+ case LS_CLOSING:
+ f->state = LS_INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case LS_STOPPING:
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ f->state = LS_STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case LS_OPENED:
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f);
+ }
+ f->state = LS_STARTING;
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_INITIAL:
+ f->state = LS_STARTING;
+ if( f->callbacks->starting ) {
+ (*f->callbacks->starting)(f);
+ }
+ break;
+
+ case LS_CLOSED:
+ if( f->flags & OPT_SILENT ) {
+ f->state = LS_STOPPED;
+ } else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ }
+ break;
+
+ case LS_CLOSING:
+ f->state = LS_STOPPING;
+ /* fall through */
+ case LS_STOPPED:
+ case LS_OPENED:
+ if( f->flags & OPT_RESTART ) {
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+#if 0 /* backport pppd 2.4.4b1; */
+/*
+ * terminate_layer - Start process of shutting down the FSM
+ *
+ * Cancel any timeout running, notify upper layers we're done, and
+ * send a terminate-request message as configured.
+ */
+static void
+terminate_layer(fsm *f, int nextstate)
+{
+ /* @todo */
+}
+#endif
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the LS_CLOSED state.
+ */
+void
+fsm_close(fsm *f, char *reason)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason));
+ switch( f->state ) {
+ case LS_STARTING:
+ f->state = LS_INITIAL;
+ break;
+ case LS_STOPPED:
+ f->state = LS_CLOSED;
+ break;
+ case LS_STOPPING:
+ f->state = LS_CLOSING;
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ case LS_OPENED:
+ if( f->state != LS_OPENED ) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ } else if( f->callbacks->down ) {
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+ }
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = LS_CLOSING;
+ break;
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(void *arg)
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case LS_CLOSING:
+ case LS_STOPPING:
+ if( f->retransmits <= 0 ) {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ } else {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ if (f->retransmits <= 0) {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ f->state = LS_STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ } else {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit) {
+ (*f->callbacks->retransmit)(f);
+ }
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == LS_ACKRCVD ) {
+ f->state = LS_REQSENT;
+ }
+ }
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(fsm *f, u_char *inpacket, int l)
+{
+ u_char *inp = inpacket;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ if (l < HEADERLEN) {
+ FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n",
+ f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n",
+ f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n",
+ f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n",
+ f->protocol, f->state, ppperr_strerr[f->state]));
+ return;
+ }
+ FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CONFNAK:
+ case CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f)));
+ if( !f->callbacks->extcode ||
+ !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ }
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
+{
+ int code, reject_if_disagree;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+ switch( f->state ) {
+ case LS_CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case LS_CLOSING:
+ case LS_STOPPING:
+ return;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ break;
+
+ case LS_STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci) { /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len) {
+ code = CONFREJ; /* Reject all CI */
+ } else {
+ code = CONFACK;
+ }
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, (u_char)code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == LS_ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = LS_OPENED;
+ if (f->callbacks->up) {
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ }
+ } else {
+ f->state = LS_ACKSENT;
+ }
+ f->nakloops = 0;
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != LS_ACKRCVD) {
+ f->state = LS_REQSENT;
+ }
+ if( code == CONFNAK ) {
+ ++f->nakloops;
+ }
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(fsm *f, int id, u_char *inp, int len)
+{
+ FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ if (id != f->reqid || f->seen_ack) { /* Expected id? */
+ return; /* Nope, toss... */
+ }
+ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
+ /* Ack is bad - ignore it */
+ FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n",
+ PROTO_NAME(f), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case LS_CLOSED:
+ case LS_STOPPED:
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+ break;
+
+ case LS_REQSENT:
+ f->state = LS_ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case LS_ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = LS_OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up) {
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ }
+ break;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
+{
+ int (*proc) (fsm *, u_char *, int);
+ int ret;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ if (id != f->reqid || f->seen_ack) { /* Expected id? */
+ return; /* Nope, toss... */
+ }
+ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (!proc || !((ret = proc(f, inp, len)))) {
+ /* Nak/reject is bad - ignore it */
+ FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n",
+ PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case LS_CLOSED:
+ case LS_STOPPED:
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0) {
+ f->state = LS_STOPPED; /* kludge for stopping CCP */
+ } else {
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ }
+ break;
+
+ case LS_ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(fsm *f, int id, u_char *p, int len)
+{
+ LWIP_UNUSED_ARG(p);
+
+ FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ switch (f->state) {
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ f->state = LS_REQSENT; /* Start over but keep trying */
+ break;
+
+ case LS_OPENED:
+ if (len > 0) {
+ FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p));
+ } else {
+ FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f)));
+ }
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ f->retransmits = 0;
+ f->state = LS_STOPPING;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(fsm *f)
+{
+ FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+
+ switch (f->state) {
+ case LS_CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = LS_CLOSED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_ACKRCVD:
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_OPENED:
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0);
+ break;
+ default:
+ FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(fsm *f, u_char *inp, int len)
+{
+ u_char code, id;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+
+ if (len < HEADERLEN) {
+ FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n",
+ PROTO_NAME(f), code, id));
+
+ if( f->state == LS_ACKRCVD ) {
+ f->state = LS_REQSENT;
+ }
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(fsm *f)
+{
+ switch( f->state ) {
+ case LS_CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case LS_CLOSED:
+ f->state = LS_CLOSED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_STOPPING:
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case LS_STOPPED:
+ f->state = LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_OPENED:
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f);
+ }
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = LS_STOPPING;
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(fsm *f, int retransmit)
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci ) {
+ (*f->callbacks->resetci)(f);
+ }
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ) {
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
+ if( f->callbacks->cilen && f->callbacks->addci ) {
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ }
+ if (f->callbacks->addci) {
+ (*f->callbacks->addci)(f, outp, &cilen);
+ }
+ } else {
+ cilen = 0;
+ }
+
+ /* send the request to our peer */
+ fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+
+ FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n",
+ PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
+{
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf[f->unit];
+ if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ }
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ }
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
+ FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n",
+ PROTO_NAME(f), code, id, outlen));
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/fsm.h b/core/lwip/src/netif/ppp/fsm.h
new file mode 100644
index 00000000..8d41b5f5
--- /dev/null
+++ b/core/lwip/src/netif/ppp/fsm.h
@@ -0,0 +1,157 @@
+/*****************************************************************************
+* fsm.h - Network Control Protocol Finite State Machine header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original based on BSD code.
+*****************************************************************************/
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.h,v 1.5 2009/12/31 17:08:08 goldsimon Exp $
+ */
+
+#ifndef FSM_H
+#define FSM_H
+
+/*
+ * LCP Packet header = Code, id, length.
+ */
+#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ 1 /* Configuration Request */
+#define CONFACK 2 /* Configuration Ack */
+#define CONFNAK 3 /* Configuration Nak */
+#define CONFREJ 4 /* Configuration Reject */
+#define TERMREQ 5 /* Termination Request */
+#define TERMACK 6 /* Termination Ack */
+#define CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ u_short protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks* callbacks; /* Callback routines */
+ char* term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci)(fsm*); /* Reset our Configuration Information */
+ int (*cilen)(fsm*); /* Length of our Configuration Information */
+ void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */
+ int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */
+ int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */
+ int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */
+ int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */
+ void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */
+ void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */
+ void (*starting)(fsm*); /* Called when we want the lower layer */
+ void (*finished)(fsm*); /* Called when we don't want the lower layer */
+ void (*protreject)(int); /* Called when Protocol-Reject received */
+ void (*retransmit)(fsm*); /* Retransmission is necessary */
+ int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */
+ char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define LS_INITIAL 0 /* Down, hasn't been opened */
+#define LS_STARTING 1 /* Down, been opened */
+#define LS_CLOSED 2 /* Up, hasn't been opened */
+#define LS_STOPPED 3 /* Open, waiting for down event */
+#define LS_CLOSING 4 /* Terminating the connection, not open */
+#define LS_STOPPING 5 /* Terminating, but open */
+#define LS_REQSENT 6 /* We've sent a Config Request */
+#define LS_ACKRCVD 7 /* We've received a Config Ack */
+#define LS_ACKSENT 8 /* We've sent a Config Ack */
+#define LS_OPENED 9 /* Connection available */
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init (fsm*);
+void fsm_lowerup (fsm*);
+void fsm_lowerdown (fsm*);
+void fsm_open (fsm*);
+void fsm_close (fsm*, char*);
+void fsm_input (fsm*, u_char*, int);
+void fsm_protreject (fsm*);
+void fsm_sdata (fsm*, u_char, u_char, u_char*, int);
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
+
+#endif /* FSM_H */
diff --git a/core/lwip/src/netif/ppp/ipcp.c b/core/lwip/src/netif/ppp/ipcp.c
new file mode 100644
index 00000000..461a600f
--- /dev/null
+++ b/core/lwip/src/netif/ppp/ipcp.c
@@ -0,0 +1,1411 @@
+/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and
+ dial-on-demand has been stripped. */
+/*****************************************************************************
+* ipcp.c - Network PPP IP Control Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "auth.h"
+#include "fsm.h"
+#include "vj.h"
+#include "ipcp.h"
+
+#include "lwip/inet.h"
+
+#include <string.h>
+
+/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+/* local vars */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci (fsm *); /* Reset our CI */
+static int ipcp_cilen (fsm *); /* Return length of our CI */
+static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */
+static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */
+static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */
+static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */
+static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipcp_up (fsm *); /* We're UP */
+static void ipcp_down (fsm *); /* We're DOWN */
+#if PPP_ADDITIONAL_CALLBACKS
+static void ipcp_script (fsm *, char *); /* Run an up/down script */
+#endif
+static void ipcp_finished (fsm *); /* Don't need lower layer */
+
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches LS_OPENED state */
+ ipcp_down, /* Called when fsm leaves LS_OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init (int);
+static void ipcp_open (int);
+static void ipcp_close (int, char *);
+static void ipcp_lowerup (int);
+static void ipcp_lowerdown (int);
+static void ipcp_input (int, u_char *, int);
+static void ipcp_protrej (int);
+
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+#if PPP_ADDITIONAL_CALLBACKS
+ ipcp_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "IPCP",
+#if PPP_ADDITIONAL_CALLBACKS
+ ip_check_options,
+ NULL,
+ ip_active_pkt
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+static void ipcp_clear_addrs (int);
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(int unit)
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->ouraddr = 0;
+#if VJ_SUPPORT
+ wo->neg_vj = 1;
+#else /* VJ_SUPPORT */
+ wo->neg_vj = 0;
+#endif /* VJ_SUPPORT */
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_SLOTS - 1;
+ wo->cflag = 0;
+ wo->default_route = 1;
+
+ ao->neg_addr = 1;
+#if VJ_SUPPORT
+ ao->neg_vj = 1;
+#else /* VJ_SUPPORT */
+ ao->neg_vj = 0;
+#endif /* VJ_SUPPORT */
+ ao->maxslotindex = MAX_SLOTS - 1;
+ ao->cflag = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(int unit)
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(int unit, char *reason)
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(int unit)
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(int unit)
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(int unit, u_char *p, int len)
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(int unit)
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(fsm *f)
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0) {
+ wo->accept_local = 1;
+ }
+ if (wo->hisaddr == 0) {
+ wo->accept_remote = 1;
+ }
+ /* Request DNS addresses from the peer */
+ wo->req_dns1 = ppp_settings.usepeerdns;
+ wo->req_dns2 = ppp_settings.usepeerdns;
+ ipcp_gotoptions[f->unit] = *wo;
+ cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(fsm *f)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ if (cis_received[f->unit] == 0) {
+ /* keep trying the new style until we see some CI from the peer */
+ go->neg_vj = 1;
+ } else {
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj) +
+ LENCIDNS(go->req_dns1) +
+ LENCIDNS(go->req_dns2));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(fsm *f, u_char *ucp, int *lenp)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+#define ADDCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = ntohl(addr); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETSHORT(cishort, p); \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) { \
+ goto bad; \
+ } \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) { \
+ goto bad; \
+ } \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u32_t l; \
+ if ((len -= addrlen) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) { \
+ goto bad; \
+ } \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) { \
+ goto bad; \
+ } \
+ } \
+ }
+
+#define ACKCIDNS(opt, neg, addr) \
+ if (neg) { \
+ u32_t l; \
+ if ((len -= CILEN_ADDR) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_ADDR || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (addr != cilong) { \
+ goto bad; \
+ } \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ return (1);
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_ackci: received bad Ack!\n"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u32_t ciaddr1, ciaddr2, l, cidnsaddr;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else { \
+ ciaddr2 = 0; \
+ } \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIDNS(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cidnsaddr = htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ IPCPDEBUG(LOG_INFO, ("local IP address %s\n",
+ inet_ntoa(ciaddr1)));
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ IPCPDEBUG(LOG_INFO, ("remote IP address %s\n",
+ inet_ntoa(ciaddr2)));
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex) {
+ try.maxslotindex = cimaxslotindex;
+ }
+ if (!cicflag) {
+ try.cflag = 0;
+ }
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ NAKCIDNS(CI_MS_DNS1, req_dns1,
+ try.dnsaddr[0] = cidnsaddr;
+ IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr)));
+ );
+
+ NAKCIDNS(CI_MS_DNS2, req_dns2,
+ try.dnsaddr[1] = cidnsaddr;
+ IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr)));
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 ) {
+ goto bad;
+ }
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ goto bad;
+ }
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS) {
+ goto bad;
+ }
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local) {
+ try.ouraddr = ciaddr1;
+ }
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote) {
+ try.hisaddr = ciaddr2;
+ }
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) {
+ goto bad;
+ }
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local) {
+ try.ouraddr = ciaddr1;
+ }
+ if (try.ouraddr != 0) {
+ try.neg_addr = 1;
+ }
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0) {
+ goto bad;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) { \
+ goto bad; \
+ } \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) { \
+ goto bad; \
+ } \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) { \
+ goto bad; \
+ } \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) { \
+ goto bad; \
+ } \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIDNS(opt, neg, dnsaddr) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != dnsaddr) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+ REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ /*
+ * Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+ return 1;
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_rejci: received bad Reject!\n"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree)
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+#ifdef OLD_CI_ADDRS
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+#endif
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u32_t tl, ciaddr1; /* Parsed address values */
+#ifdef OLD_CI_ADDRS
+ u32_t ciaddr2; /* Parsed address values */
+#endif
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ cis_received[f->unit] = 1;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = (u_short)l;/* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+#ifdef OLD_CI_ADDRS /* Need to save space... */
+ case CI_ADDRS:
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n"));
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2)));
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+#endif
+
+ case CI_ADDR:
+ if (!ao->neg_addr) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ } else if (cilen != CILEN_ADDR) { /* Check CI length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1)));
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1)));
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1)));
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n",
+ d+1, inet_ntoa(tl)));
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1));
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ if (!ao->neg_vj) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen));
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort));
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex));
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag));
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_SLOTS - 1;
+ ho->cflag = 1;
+ }
+ IPCPDEBUG(LOG_INFO, (
+ "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n",
+ ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag));
+ break;
+
+ default:
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) { /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+ }
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) { /* Getting fed up with sending NAKs? */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n"));
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) { /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ }
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip) {
+ BCOPY(cip, ucp, cilen); /* Move it */
+ }
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n"));
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = (int)(ucp - inp); /* Compute output length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+#if 0
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options(u_long localAddr)
+{
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Load our default IP address but allow the remote host to give us
+ * a new address.
+ */
+ if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) {
+ wo->accept_local = 1; /* don't insist on this default value */
+ wo->ouraddr = htonl(localAddr);
+ }
+}
+#endif
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(fsm *f)
+{
+ u32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ np_up(f->unit, PPP_IP);
+ IPCPDEBUG(LOG_INFO, ("ipcp: up\n"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr) {
+ ho->hisaddr = wo->hisaddr;
+ }
+
+ if (ho->hisaddr == 0) {
+ IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n"));
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n"));
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+
+ if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+ /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/
+ }
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n",
+ inet_ntoa(ho->hisaddr)));
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ /* set tcp compression */
+ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) {
+ IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ IPCPDEBUG(LOG_WARNING, ("sifup failed\n"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route) {
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) {
+ default_route_set[f->unit] = 1;
+ }
+ }
+
+ IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr)));
+ IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr)));
+ if (go->dnsaddr[0]) {
+ IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0])));
+ }
+ if (go->dnsaddr[1]) {
+ IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1])));
+ }
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(fsm *f)
+{
+ IPCPDEBUG(LOG_INFO, ("ipcp: down\n"));
+ np_down(f->unit, PPP_IP);
+ sifvjcomp(f->unit, 0, 0, 0);
+
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit);
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes, etc.
+ */
+static void
+ipcp_clear_addrs(int unit)
+{
+ u32_t ouraddr, hisaddr;
+
+ ouraddr = ipcp_gotoptions[unit].ouraddr;
+ hisaddr = ipcp_hisoptions[unit].hisaddr;
+ if (default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(fsm *f)
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static int
+ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(plen);
+ LWIP_UNUSED_ARG(printer);
+ LWIP_UNUSED_ARG(arg);
+ return 0;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(u_char *pkt, int len)
+{
+ u_char *tcp;
+ int hlen;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN) {
+ return 0;
+ }
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0) {
+ return 0;
+ }
+ if (get_ipproto(pkt) != IPPROTO_TCP) {
+ return 1;
+ }
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN) {
+ return 0;
+ }
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) {
+ return 0;
+ }
+ return 1;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/ipcp.h b/core/lwip/src/netif/ppp/ipcp.h
new file mode 100644
index 00000000..de03f460
--- /dev/null
+++ b/core/lwip/src/netif/ppp/ipcp.h
@@ -0,0 +1,106 @@
+/*****************************************************************************
+* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $
+ */
+
+#ifndef IPCP_H
+#define IPCP_H
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS1 128 /* Primary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#define CI_MS_WINS2 130 /* Secondary WINS value */
+
+#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
+ /* maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */
+#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
+ /* compression option */
+
+typedef struct ipcp_options {
+ u_int neg_addr : 1; /* Negotiate IP Address? */
+ u_int old_addrs : 1; /* Use old (IP-Addresses) option? */
+ u_int req_addr : 1; /* Ask peer to send IP address? */
+ u_int default_route : 1; /* Assign default route through interface? */
+ u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */
+ u_int neg_vj : 1; /* Van Jacobson Compression? */
+ u_int old_vj : 1; /* use old (short) form of VJ option? */
+ u_int accept_local : 1; /* accept peer's value for ouraddr */
+ u_int accept_remote : 1; /* accept peer's value for hisaddr */
+ u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */
+ u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ u_char maxslotindex; /* VJ slots - 1. */
+ u_char cflag; /* VJ slot compression flag. */
+ u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+ u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+ u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+extern struct protent ipcp_protent;
+
+#endif /* IPCP_H */
diff --git a/core/lwip/src/netif/ppp/lcp.c b/core/lwip/src/netif/ppp/lcp.c
new file mode 100644
index 00000000..21c83ac4
--- /dev/null
+++ b/core/lwip/src/netif/ppp/lcp.c
@@ -0,0 +1,2066 @@
+/*****************************************************************************
+* lcp.c - Network Link Control Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+#include "chap.h"
+#include "magic.h"
+#include "auth.h"
+#include "lcp.h"
+
+#include <string.h>
+
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#else
+#define PPPOE_MAXMTU PPP_MAXMRU
+#endif
+
+#if 0 /* UNUSED */
+/*
+ * LCP-related command-line options.
+ */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+bool lax_recv = 0; /* accept control chars in asyncmap */
+
+static int setescape (char **);
+
+static option_t lcp_option_list[] = {
+ /* LCP options */
+ /* list stripped for simplicity */
+ {NULL}
+};
+#endif /* UNUSED */
+
+/* options */
+LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
+static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */
+static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */
+
+/* global vars */
+static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */
+
+static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static u32_t lcp_echo_number = 0; /* ID number of next echo frame */
+static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */
+
+/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */
+static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci (fsm*); /* Reset our CI */
+static int lcp_cilen (fsm*); /* Return length of our CI */
+static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */
+static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */
+static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */
+static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */
+static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */
+static void lcp_up (fsm*); /* We're UP */
+static void lcp_down (fsm*); /* We're DOWN */
+static void lcp_starting (fsm*); /* We need lower layer up */
+static void lcp_finished (fsm*); /* We need lower layer down */
+static int lcp_extcode (fsm*, int, u_char, u_char*, int);
+static void lcp_rprotrej (fsm*, u_char*, int);
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup (int);
+static void lcp_echo_lowerdown (int);
+static void LcpEchoTimeout (void*);
+static void lcp_received_echo_reply (fsm*, int, u_char*, int);
+static void LcpSendEchoRequest (fsm*);
+static void LcpLinkFailure (fsm*);
+static void LcpEchoCheck (fsm*);
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches LS_OPENED state */
+ lcp_down, /* Called when fsm leaves LS_OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_input (int, u_char *, int);
+static void lcp_protrej (int);
+
+struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+#if PPP_ADDITIONAL_CALLBACKS
+ lcp_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "LCP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */
+#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */
+#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ")
+
+#if 0 /* UNUSED */
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'", p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+#endif /* UNUSED */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+void
+lcp_init(int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ wo->passive = 0;
+ wo->silent = 0;
+ wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */
+ wo->neg_mru = 1;
+ wo->mru = PPP_DEFMRU;
+ wo->neg_asyncmap = 1;
+ wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
+ wo->neg_chap = 0; /* Set to 1 on server */
+ wo->neg_upap = 0; /* Set to 1 on server */
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+ wo->neg_lqr = 0; /* no LQR implementation yet */
+ wo->neg_cbcp = 0;
+
+ ao->neg_mru = 1;
+ ao->mru = PPP_MAXMRU;
+ ao->neg_asyncmap = 1;
+ ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
+ ao->neg_chap = (CHAP_SUPPORT != 0);
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = (PAP_SUPPORT != 0);
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+ ao->neg_lqr = 0; /* no LQR implementation yet */
+ ao->neg_cbcp = (CBCP_SUPPORT != 0);
+
+ /*
+ * Set transmit escape for the flag and escape characters plus anything
+ * set for the allowable options.
+ */
+ memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+ xmit_accm[unit][15] = 0x60;
+ xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF));
+ xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF);
+ xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF);
+ xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF);
+ LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n",
+ xmit_accm[unit][0],
+ xmit_accm[unit][1],
+ xmit_accm[unit][2],
+ xmit_accm[unit][3]));
+
+ lcp_phase[unit] = PHASE_INITIALIZE;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags = 0;
+ if (wo->passive) {
+ f->flags |= OPT_PASSIVE;
+ }
+ if (wo->silent) {
+ f->flags |= OPT_SILENT;
+ }
+ fsm_open(f);
+
+ lcp_phase[unit] = PHASE_ESTABLISH;
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(int unit, char *reason)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_phase[unit] != PHASE_DEAD) {
+ lcp_phase[unit] = PHASE_TERMINATE;
+ }
+ if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do an
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = LS_CLOSED;
+ lcp_finished(f);
+ } else {
+ fsm_close(f, reason);
+ }
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(int unit)
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_set_xaccm(unit, &xmit_accm[unit]);
+ ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0);
+ ppp_recv_config(unit, PPP_MRU, 0x00000000l,
+ wo->neg_pcompression, wo->neg_accompression);
+ peer_mru[unit] = PPP_MRU;
+ lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0]
+ | ((u_long)xmit_accm[unit][1] << 8)
+ | ((u_long)xmit_accm[unit][2] << 16)
+ | ((u_long)xmit_accm[unit][3] << 24);
+ LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n",
+ xmit_accm[unit][3],
+ xmit_accm[unit][2],
+ xmit_accm[unit][1],
+ xmit_accm[unit][0]));
+
+ fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(int unit)
+{
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(int unit, u_char *p, int len)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len)
+{
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != LS_OPENED) {
+ break;
+ }
+ LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id));
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(fsm *f, u_char *inp, int len)
+{
+ int i;
+ struct protent *protp;
+ u_short prot;
+
+ if (len < (int)sizeof (u_short)) {
+ LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n"));
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot));
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * LS_OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != LS_OPENED ) {
+ LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state));
+ return;
+ }
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol == prot && protp->enabled_flag) {
+ (*protp->protrej)(f->unit);
+ return;
+ }
+ }
+
+ LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot));
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+static void
+lcp_protrej(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+ /*
+ * Can't reject LCP!
+ */
+ LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n"));
+ fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(int unit, u_char *p, int len)
+{
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the LS_OPENED state.
+ */
+
+ fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(fsm *f)
+{
+ lcp_wantoptions[f->unit].magicnumber = magic();
+ lcp_wantoptions[f->unit].numloops = 0;
+ lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
+ peer_mru[f->unit] = PPP_MRU;
+ auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(fsm *f)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP and UPAP, even if we will
+ * accept either.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) +
+ LENCICHAP(go->neg_chap) +
+ LENCISHORT(!go->neg_chap && go->neg_upap) +
+ LENCILQR(go->neg_lqr) +
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(fsm *f, u_char *ucp, int *lenp)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#define ADDCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(val, ucp); \
+ PUTCHAR(digest, ucp); \
+ }
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n"));
+ }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+lcp_ackci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cilen, citype, cichar;
+ u_short cishort;
+ u32_t cilong;
+
+ /*
+ * CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || citype != opt) \
+ goto bad; \
+ }
+#define ACKCISHORT(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+#define ACKCICHAR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_CHAR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR || citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != val) \
+ goto bad; \
+ }
+#define ACKCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ if ((len -= CILEN_CHAP) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAP || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != digest) \
+ goto bad; \
+ }
+#define ACKCILONG(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LONG) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LONG || citype != opt) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCILQR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LQR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LQR || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != PPP_LQR) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n"));
+ return (1);
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_acki: received bad Ack!\n"));
+ return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+lcp_nakci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ u_char citype, cichar, *next;
+ u_short cishort;
+ u32_t cilong;
+ lcp_options no; /* options we've seen Naks for */
+ lcp_options try; /* options to request next time */
+ int looped_back = 0;
+ int cilen;
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIVOID(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAP(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCISHORT(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILONG(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILQR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * We don't care if they want to send us smaller packets than
+ * we want. Therefore, accept any MRU less than what we asked for,
+ * but then ignore the new value when setting the MRU in the kernel.
+ * If they send us a bigger MRU than what we asked, accept it, up to
+ * the limit of the default MRU we'd get if we didn't negotiate.
+ */
+ if (go->neg_mru && go->mru != PPP_DEFMRU) {
+ NAKCISHORT(CI_MRU, neg_mru,
+ if (cishort <= wo->mru || cishort < PPP_DEFMRU) {
+ try.mru = cishort;
+ }
+ );
+ }
+
+ /*
+ * Add any characters they want to our (receive-side) asyncmap.
+ */
+ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) {
+ NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+ try.asyncmap = go->asyncmap | cilong;
+ );
+ }
+
+ /*
+ * If they've nak'd our authentication-protocol, check whether
+ * they are proposing a different protocol, or a different
+ * hash algorithm for CHAP.
+ */
+ if ((go->neg_chap || go->neg_upap)
+ && len >= CILEN_SHORT
+ && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+ no.neg_chap = go->neg_chap;
+ no.neg_upap = go->neg_upap;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+ /*
+ * If we were asking for CHAP, they obviously don't want to do it.
+ * If we weren't asking for CHAP, then we were asking for PAP,
+ * in which case this Nak is bad.
+ */
+ if (!go->neg_chap) {
+ goto bad;
+ }
+ try.neg_chap = 0;
+
+ } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+ GETCHAR(cichar, p);
+ if (go->neg_chap) {
+ /*
+ * We were asking for CHAP/MD5; they must want a different
+ * algorithm. If they can't do MD5, we'll have to stop
+ * asking for CHAP.
+ */
+ if (cichar != go->chap_mdtype) {
+ try.neg_chap = 0;
+ }
+ } else {
+ /*
+ * Stop asking for PAP if we were asking for it.
+ */
+ try.neg_upap = 0;
+ }
+
+ } else {
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ if (go->neg_chap) {
+ try.neg_chap = 0;
+ } else {
+ try.neg_upap = 0;
+ }
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+ /*
+ * If they can't cope with our link quality protocol, we'll have
+ * to stop asking for LQR. We haven't got any other protocol.
+ * If they Nak the reporting period, take their value XXX ?
+ */
+ NAKCILQR(CI_QUALITY, neg_lqr,
+ if (cishort != PPP_LQR) {
+ try.neg_lqr = 0;
+ } else {
+ try.lqr_period = cilong;
+ }
+ );
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try.neg_cbcp = 0;
+ );
+
+ /*
+ * Check for a looped-back line.
+ */
+ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+ try.magicnumber = magic();
+ looped_back = 1;
+ );
+
+ /*
+ * Peer shouldn't send Nak for protocol compression or
+ * address/control compression requests; they should send
+ * a Reject instead. If they send a Nak, treat it as a Reject.
+ */
+ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
+ try.neg_pcompression = 0;
+ );
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
+ try.neg_accompression = 0;
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If we see an option that we requested, or one we've already seen
+ * in this packet, then this packet is bad.
+ * If we wanted to respond by starting to negotiate on the requested
+ * option(s), we could, but we don't, because except for the
+ * authentication type and quality protocol, if we are not negotiating
+ * an option, it is because we were told not to.
+ * For the authentication type, the Nak from the peer means
+ * `let me authenticate myself with you' which is a bit pointless.
+ * For the quality protocol, the Nak means `ask me to send you quality
+ * reports', but if we didn't ask for them, we don't want them.
+ * An option we don't recognize represents the peer asking to
+ * negotiate some option we don't support, so ignore it.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (cilen < CILEN_VOID || (len -= cilen) < 0) {
+ goto bad;
+ }
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_MRU:
+ if ((go->neg_mru && go->mru != PPP_DEFMRU)
+ || no.neg_mru || cilen != CILEN_SHORT) {
+ goto bad;
+ }
+ GETSHORT(cishort, p);
+ if (cishort < PPP_DEFMRU) {
+ try.mru = cishort;
+ }
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl)
+ || no.neg_asyncmap || cilen != CILEN_LONG) {
+ goto bad;
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) {
+ goto bad;
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (go->neg_magicnumber || no.neg_magicnumber ||
+ cilen != CILEN_LONG) {
+ goto bad;
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (go->neg_pcompression || no.neg_pcompression
+ || cilen != CILEN_VOID) {
+ goto bad;
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (go->neg_accompression || no.neg_accompression
+ || cilen != CILEN_VOID) {
+ goto bad;
+ }
+ break;
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) {
+ goto bad;
+ }
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0) {
+ goto bad;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ if (looped_back) {
+ if (++try.numloops >= lcp_loopbackfail) {
+ LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n"));
+ lcp_close(f->unit, "Loopback detected");
+ }
+ } else {
+ try.numloops = 0;
+ }
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_nakci: received bad Nak!\n"));
+ return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int
+lcp_rejci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cichar;
+ u_short cishort;
+ u32_t cilong;
+ lcp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \
+ }
+#define REJCISHORT(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \
+ }
+#define REJCICHAP(opt, neg, val, digest) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cishort != val || cichar != digest) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ try.neg_upap = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \
+ }
+#define REJCILONG(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \
+ }
+#define REJCILQR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cishort != PPP_LQR || cilong != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \
+ }
+#define REJCICBCP(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CBCP && \
+ p[1] == CILEN_CBCP && \
+ p[0] == opt) { \
+ len -= CILEN_CBCP; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+ REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
+ if (!go->neg_chap) {
+ REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+ }
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ /*
+ * Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+ return 1;
+
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_rejci: received bad Reject!\n"));
+ return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(fsm *f,
+ u_char *inp, /* Requested CIs */
+ int *lenp, /* Length of requested CIs */
+ int reject_if_disagree)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ int cilen, citype; /* Parsed len, type */
+ u_char cichar; /* Parsed char value */
+ u_short cishort; /* Parsed short value */
+ u32_t cilong; /* Parse long value */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *rejp; /* Pointer to next char in reject frame */
+ u_char *nakp; /* Pointer to next char in Nak frame */
+ int l = *lenp; /* Length left */
+#if TRACELCP > 0
+ char traceBuf[80];
+ size_t traceNdx = 0;
+#endif
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ nakp = nak_buffer;
+ rejp = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ citype = 0;
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_MRU:
+ if (!ao->neg_mru) { /* Allow option? */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ } else if (cilen != CILEN_SHORT) { /* Check CI length */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETSHORT(cishort, p); /* Parse MRU */
+
+ /*
+ * He must be able to receive at least our minimum.
+ * No need to check a maximum. If he sends a large number,
+ * we'll just ignore it.
+ */
+ if (cishort < PPP_MINMRU) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n"));
+ orc = CONFNAK; /* Nak CI */
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */
+ break;
+ }
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+
+ case CI_ASYNCMAP:
+ if (!ao->neg_asyncmap) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_LONG) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n"));
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+
+ /*
+ * Asyncmap must have set at least the bits
+ * which are set in lcp_allowoptions[unit].asyncmap.
+ */
+ if ((ao->asyncmap & ~cilong) != 0) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n",
+ cilong, ao->asyncmap));
+ orc = CONFNAK;
+ PUTCHAR(CI_ASYNCMAP, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(ao->asyncmap | cilong, nakp);
+ break;
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+
+ case CI_AUTHTYPE:
+ if (cilen < CILEN_SHORT) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n"));
+ orc = CONFREJ;
+ break;
+ } else if (!(ao->neg_upap || ao->neg_chap)) {
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n"));
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ /*
+ * Authtype must be UPAP or CHAP.
+ *
+ * Note: if both ao->neg_upap and ao->neg_chap are set,
+ * and the peer sends a Configure-Request with two
+ * authenticate-protocol requests, one for CHAP and one
+ * for UPAP, then we will reject the second request.
+ * Whether we end up doing CHAP or UPAP depends then on
+ * the ordering of the CIs in the peer's Configure-Request.
+ */
+
+ if (cishort == PPP_PAP) {
+ if (ho->neg_chap) { /* we've already accepted CHAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_SHORT) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n"));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_upap) { /* we don't want to do PAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n"));
+ orc = CONFNAK; /* NAK it and suggest CHAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+ ho->neg_upap = 1;
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+ }
+ if (cishort == PPP_CHAP) {
+ if (ho->neg_upap) { /* we've already accepted PAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_CHAP) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n"));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_chap) { /* we don't want to do CHAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n"));
+ orc = CONFNAK; /* NAK it and suggest PAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ break;
+ }
+ GETCHAR(cichar, p); /* get digest type*/
+ if (cichar != CHAP_DIGEST_MD5
+#if MSCHAP_SUPPORT
+ && cichar != CHAP_MICROSOFT
+#endif
+ ) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar));
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar);
+ traceNdx = strlen(traceBuf);
+#endif
+ ho->chap_mdtype = cichar; /* save md type */
+ ho->neg_chap = 1;
+ break;
+ }
+
+ /*
+ * We don't recognize the protocol they're asking for.
+ * Nak it with something we're willing to do.
+ * (At this point we know ao->neg_upap || ao->neg_chap.)
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ if (ao->neg_chap) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort));
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ } else {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort));
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ }
+ break;
+
+ case CI_QUALITY:
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+
+ if (!ao->neg_lqr ||
+ cilen != CILEN_LQR) {
+ orc = CONFREJ;
+ break;
+ }
+
+ /*
+ * Check the protocol and the reporting period.
+ * XXX When should we Nak this, and what with?
+ */
+ if (cishort != PPP_LQR) {
+ orc = CONFNAK;
+ PUTCHAR(CI_QUALITY, nakp);
+ PUTCHAR(CILEN_LQR, nakp);
+ PUTSHORT(PPP_LQR, nakp);
+ PUTLONG(ao->lqr_period, nakp);
+ break;
+ }
+ break;
+
+ case CI_MAGICNUMBER:
+ if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+
+ /*
+ * He must have a different magic number.
+ */
+ if (go->neg_magicnumber &&
+ cilong == go->magicnumber) {
+ cilong = magic(); /* Don't put magic() inside macro! */
+ orc = CONFNAK;
+ PUTCHAR(CI_MAGICNUMBER, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(cilong, nakp);
+ break;
+ }
+ ho->neg_magicnumber = 1;
+ ho->magicnumber = cilong;
+ break;
+
+
+ case CI_PCOMPRESSION:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION");
+ traceNdx = strlen(traceBuf);
+#endif
+ if (!ao->neg_pcompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION");
+ traceNdx = strlen(traceBuf);
+#endif
+ if (!ao->neg_accompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_accompression = 1;
+ break;
+
+ case CI_MRRU:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ case CI_SSNHF:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ case CI_EPDISC:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ default:
+#if TRACELCP
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype);
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+ }
+
+ endswitch:
+#if TRACELCP
+ if (traceNdx >= 80 - 32) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf));
+ traceNdx = 0;
+ }
+#endif
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) { /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+ }
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree /* Getting fed up with sending NAKs? */
+ && citype != CI_MAGICNUMBER) {
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) { /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ }
+ rc = CONFNAK;
+ }
+ }
+ if (orc == CONFREJ) { /* Reject this CI */
+ rc = CONFREJ;
+ if (cip != rejp) { /* Need to move rejected CI? */
+ BCOPY(cip, rejp, cilen); /* Move it */
+ }
+ INCPTR(cilen, rejp); /* Update output pointer */
+ }
+ }
+
+ /*
+ * If we wanted to send additional NAKs (for unsent CIs), the
+ * code would go here. The extra NAKs would go at *nakp.
+ * At present there are no cases where we want to ask the
+ * peer to negotiate an option.
+ */
+
+ switch (rc) {
+ case CONFACK:
+ *lenp = (int)(next - inp);
+ break;
+ case CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+ */
+ *lenp = (int)(nakp - nak_buffer);
+ BCOPY(nak_buffer, inp, *lenp);
+ break;
+ case CONFREJ:
+ *lenp = (int)(rejp - inp);
+ break;
+ }
+
+#if TRACELCP > 0
+ if (traceNdx > 0) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf));
+ }
+#endif
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(fsm *f)
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+
+ if (!go->neg_magicnumber) {
+ go->magicnumber = 0;
+ }
+ if (!ho->neg_magicnumber) {
+ ho->magicnumber = 0;
+ }
+
+ /*
+ * Set our MTU to the smaller of the MTU we wanted and
+ * the MRU our peer wanted. If we negotiated an MRU,
+ * set our MRU to the larger of value we wanted and
+ * the value we got in the negotiation.
+ */
+ ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)),
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl),
+ ho->neg_pcompression, ho->neg_accompression);
+ /*
+ * If the asyncmap hasn't been negotiated, we really should
+ * set the receive asyncmap to ffffffff, but we set it to 0
+ * for backwards contemptibility.
+ */
+ ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU),
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ go->neg_pcompression, go->neg_accompression);
+
+ if (ho->neg_mru) {
+ peer_mru[f->unit] = ho->mru;
+ }
+
+ lcp_echo_lowerup(f->unit); /* Enable echo messages */
+
+ link_established(f->unit); /* The link is up; authenticate now */
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(fsm *f)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+ lcp_echo_lowerdown(f->unit);
+
+ link_down(f->unit);
+
+ ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0);
+ ppp_recv_config(f->unit, PPP_MRU,
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ go->neg_pcompression, go->neg_accompression);
+ peer_mru[f->unit] = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(fsm *f)
+{
+ link_required(f->unit); /* lwip: currently does nothing */
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(fsm *f)
+{
+ link_terminated(f->unit); /* we are finished with the link */
+}
+
+
+#if PPP_ADDITIONAL_CALLBACKS
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+static void
+print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg)
+{
+ int c;
+
+ printer(arg, "\"");
+ for (; len > 0; --len) {
+ c = *p++;
+ if (' ' <= c && c <= '~') {
+ if (c == '\\' || c == '"') {
+ printer(arg, "\\");
+ }
+ printer(arg, "%c", c);
+ } else {
+ switch (c) {
+ case '\n':
+ printer(arg, "\\n");
+ break;
+ case '\r':
+ printer(arg, "\\r");
+ break;
+ case '\t':
+ printer(arg, "\\t");
+ break;
+ default:
+ printer(arg, "\\%.3o", c);
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static char *lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq"
+};
+
+static int
+lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u32_t cilong;
+
+ if (plen < HEADERLEN) {
+ return 0;
+ }
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen) {
+ return 0;
+ }
+
+ if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) {
+ printer(arg, " %s", lcp_codenames[code-1]);
+ } else {
+ printer(arg, " code=0x%x", code);
+ }
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_MRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mru %d", cishort);
+ }
+ break;
+ case CI_ASYNCMAP:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "asyncmap 0x%lx", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+ case PPP_CHAP:
+ printer(arg, "chap");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_QUALITY:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "quality ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_LQR:
+ printer(arg, "lqr");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case CBCP_OPT:
+ printer(arg, "CBCP");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "magic 0x%x", cilong);
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "pcomp");
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "accomp");
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char*)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case ECHOREQ:
+ case ECHOREP:
+ case DISCREQ:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ p += 4;
+ len -= 4;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return (int)(p - pstart);
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+static void
+LcpLinkFailure (fsm *f)
+{
+ if (f->state == LS_OPENED) {
+ LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending));
+ LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n"));
+ lcp_close(f->unit, "Peer not responding");
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+static void
+LcpEchoCheck (fsm *f)
+{
+ LcpSendEchoRequest (f);
+
+ /*
+ * Start the timer for the next interval.
+ */
+ LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0);
+
+ TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+ lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+static void
+LcpEchoTimeout (void *arg)
+{
+ if (lcp_echo_timer_running != 0) {
+ lcp_echo_timer_running = 0;
+ LcpEchoCheck ((fsm *) arg);
+ }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+static void
+lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len)
+{
+ u32_t magic;
+
+ LWIP_UNUSED_ARG(id);
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len));
+ return;
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) {
+ LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n"));
+ return;
+ }
+
+ /* Reset the number of outstanding echo frames */
+ lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+static void
+LcpSendEchoRequest (fsm *f)
+{
+ u32_t lcp_magic;
+ u_char pkt[4], *pktp;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if (lcp_echo_fails != 0) {
+ if (lcp_echos_pending >= lcp_echo_fails) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ }
+ }
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == LS_OPENED) {
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt));
+ ++lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (lcp_echo_interval != 0) {
+ LcpEchoCheck (f);
+ }
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/lcp.h b/core/lwip/src/netif/ppp/lcp.h
new file mode 100644
index 00000000..b9201eeb
--- /dev/null
+++ b/core/lwip/src/netif/ppp/lcp.h
@@ -0,0 +1,151 @@
+/*****************************************************************************
+* lcp.h - Network Link Control Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $
+ */
+
+#ifndef LCP_H
+#define LCP_H
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_CALLBACK 13 /* callback */
+#define CI_MRRU 17 /* max reconstructed receive unit; multilink */
+#define CI_SSNHF 18 /* short sequence numbers for multilink */
+#define CI_EPDISC 19 /* endpoint discriminator */
+
+/*
+ * LCP-specific packet types (code numbers).
+ */
+#define PROTREJ 8 /* Protocol Reject */
+#define ECHOREQ 9 /* Echo Request */
+#define ECHOREP 10 /* Echo Reply */
+#define DISCREQ 11 /* Discard Request */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ u_int passive : 1; /* Don't die if we don't get a response */
+ u_int silent : 1; /* Wait for the other end to start first */
+ u_int restart : 1; /* Restart vs. exit after close */
+ u_int neg_mru : 1; /* Negotiate the MRU? */
+ u_int neg_asyncmap : 1; /* Negotiate the async map? */
+ u_int neg_upap : 1; /* Ask for UPAP authentication? */
+ u_int neg_chap : 1; /* Ask for CHAP authentication? */
+ u_int neg_magicnumber : 1; /* Ask for magic number? */
+ u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */
+ u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
+ u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */
+ u_int neg_cbcp : 1; /* Negotiate use of CBCP */
+#ifdef PPP_MULTILINK
+ u_int neg_mrru : 1; /* Negotiate multilink MRRU */
+ u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */
+ u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */
+#endif
+ u_short mru; /* Value of MRU */
+#ifdef PPP_MULTILINK
+ u_short mrru; /* Value of MRRU, and multilink enable */
+#endif
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u32_t asyncmap; /* Value of async map */
+ u32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+#ifdef PPP_MULTILINK
+ struct epdisc endpoint; /* endpoint discriminator */
+#endif
+} lcp_options;
+
+/*
+ * Values for phase from BSD pppd.h based on RFC 1661.
+ */
+typedef enum {
+ PHASE_DEAD = 0,
+ PHASE_INITIALIZE,
+ PHASE_ESTABLISH,
+ PHASE_AUTHENTICATE,
+ PHASE_CALLBACK,
+ PHASE_NETWORK,
+ PHASE_TERMINATE
+} LinkPhase;
+
+
+
+extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+extern ext_accm xmit_accm[];
+
+
+void lcp_init (int);
+void lcp_open (int);
+void lcp_close (int, char *);
+void lcp_lowerup (int);
+void lcp_lowerdown(int);
+void lcp_sprotrej (int, u_char *, int); /* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
+
+#endif /* LCP_H */
diff --git a/core/lwip/src/netif/ppp/magic.c b/core/lwip/src/netif/ppp/magic.c
new file mode 100644
index 00000000..39013308
--- /dev/null
+++ b/core/lwip/src/netif/ppp/magic.c
@@ -0,0 +1,80 @@
+/*****************************************************************************
+* magic.c - Network Random Number Generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD magic.c.
+*****************************************************************************/
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT
+
+#include "ppp.h"
+#include "randm.h"
+#include "magic.h"
+
+
+/*
+ * magicInit - Initialize the magic number generator.
+ *
+ * Since we use another random number generator that has its own
+ * initialization, we do nothing here.
+ */
+void magicInit()
+{
+ return;
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u32_t magic()
+{
+ return avRandom();
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/magic.h b/core/lwip/src/netif/ppp/magic.h
new file mode 100644
index 00000000..eba70d20
--- /dev/null
+++ b/core/lwip/src/netif/ppp/magic.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+* magic.h - Network Random Number Generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: magic.h,v 1.3 2010/01/18 20:49:43 goldsimon Exp $
+ */
+
+#ifndef MAGIC_H
+#define MAGIC_H
+
+/* Initialize the magic number generator */
+void magicInit(void);
+
+/* Returns the next magic number */
+u32_t magic(void);
+
+#endif /* MAGIC_H */
diff --git a/core/lwip/src/netif/ppp/md5.c b/core/lwip/src/netif/ppp/md5.c
new file mode 100644
index 00000000..3cb69e2b
--- /dev/null
+++ b/core/lwip/src/netif/ppp/md5.c
@@ -0,0 +1,320 @@
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if CHAP_SUPPORT || MD5_SUPPORT
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "md5.h"
+
+#include <string.h>
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform (u32_t *buf, u32_t *in);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+#ifdef __STDC__
+#define UL(x) x##UL
+#else
+#ifdef WIN32
+#define UL(x) x##UL
+#else
+#define UL(x) x
+#endif
+#endif
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void
+MD5Init (MD5_CTX *mdContext)
+{
+ mdContext->i[0] = mdContext->i[1] = (u32_t)0;
+
+ /* Load magic initialization constants. */
+ mdContext->buf[0] = (u32_t)0x67452301UL;
+ mdContext->buf[1] = (u32_t)0xefcdab89UL;
+ mdContext->buf[2] = (u32_t)0x98badcfeUL;
+ mdContext->buf[3] = (u32_t)0x10325476UL;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void
+MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen)
+{
+ u32_t in[16];
+ int mdi;
+ unsigned int i, ii;
+
+#if 0
+ PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf));
+ PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf));
+#endif
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) {
+ mdContext->i[1]++;
+ }
+ mdContext->i[0] += ((u32_t)inLen << 3);
+ mdContext->i[1] += ((u32_t)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4) {
+ in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
+ (((u32_t)mdContext->in[ii+2]) << 16) |
+ (((u32_t)mdContext->in[ii+1]) << 8) |
+ ((u32_t)mdContext->in[ii]);
+ }
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+void
+MD5Final (unsigned char hash[], MD5_CTX *mdContext)
+{
+ u32_t in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4) {
+ in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
+ (((u32_t)mdContext->in[ii+2]) << 16) |
+ (((u32_t)mdContext->in[ii+1]) << 8) |
+ ((u32_t)mdContext->in[ii]);
+ }
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+ SMEMCPY(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void
+Transform (u32_t *buf, u32_t *in)
+{
+ u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+ FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+ FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+ FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+ FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+ FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+ FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+ FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+ GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+ GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */
+ GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+ GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+ GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+ GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+ HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+ HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+ HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+ HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+ HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+ HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+ II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+ II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+ II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+ II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+ II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+ II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif /* CHAP_SUPPORT || MD5_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/md5.h b/core/lwip/src/netif/ppp/md5.h
new file mode 100644
index 00000000..e129533f
--- /dev/null
+++ b/core/lwip/src/netif/ppp/md5.h
@@ -0,0 +1,55 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ u32_t i[2]; /* number of _bits_ handled mod 2^64 */
+ u32_t buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ( MD5_CTX *mdContext);
+void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
+void MD5Final ( unsigned char hash[], MD5_CTX *mdContext);
+
+#endif /* MD5_H */
diff --git a/core/lwip/src/netif/ppp/pap.c b/core/lwip/src/netif/ppp/pap.c
new file mode 100644
index 00000000..ac44e646
--- /dev/null
+++ b/core/lwip/src/netif/ppp/pap.c
@@ -0,0 +1,628 @@
+/*****************************************************************************
+* pap.c - Network Password Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-12 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "auth.h"
+#include "pap.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+static bool hide_password = 1;
+
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+ { "hide-password", o_bool, &hide_password,
+ "Don't output passwords to log", 1 },
+ { "show-password", o_bool, &hide_password,
+ "Show password string in debug log messages", 0 },
+ { "pap-restart", o_int, &upap[0].us_timeouttime,
+ "Set retransmit timeout for PAP" },
+ { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+ "Set max number of transmissions for auth-reqs" },
+ { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+ "Set time limit for peer PAP authentication" },
+ { NULL }
+};
+#endif
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init (int);
+static void upap_lowerup (int);
+static void upap_lowerdown (int);
+static void upap_input (int, u_char *, int);
+static void upap_protrej (int);
+#if PPP_ADDITIONAL_CALLBACKS
+static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+#if PPP_ADDITIONAL_CALLBACKS
+ upap_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "PAP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout (void *);
+static void upap_reqtimeout(void *);
+static void upap_rauthreq (upap_state *, u_char *, u_char, int);
+static void upap_rauthack (upap_state *, u_char *, int, int);
+static void upap_rauthnak (upap_state *, u_char *, int, int);
+static void upap_sauthreq (upap_state *);
+static void upap_sresp (upap_state *, u_char, u_char, char *, int);
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit));
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(int unit, char *user, char *password)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n",
+ unit, user, password, u->us_clientstate));
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = (int)strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = (int)strlen(password);
+
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0) {
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(void *arg)
+{
+ upap_state *u = (upap_state *) arg;
+
+ UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n",
+ u->us_unit, u->us_timeouttime, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) {
+ UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n"));
+ return;
+ }
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n"));
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(void *arg)
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN) {
+ return; /* huh?? */
+ }
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate));
+
+ if (u->us_clientstate == UPAPCS_INITIAL) {
+ u->us_clientstate = UPAPCS_CLOSED;
+ } else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ /* now client state is UPAPCS__AUTHREQ */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL) {
+ u->us_serverstate = UPAPSS_CLOSED;
+ } else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0) {
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate));
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) {
+ UNTIMEOUT(upap_reqtimeout, u);
+ }
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n"));
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n"));
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(int unit, u_char *inpacket, int l)
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < (int)UPAP_HEADERLEN) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < (int)UPAP_HEADERLEN) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n"));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n"));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len));
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len)
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ u_char retcode;
+ char *msg;
+ int msglen;
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id));
+
+ if (u->us_serverstate < UPAPSS_LISTEN) {
+ return;
+ }
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < (int)sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen);
+ /* lwip: currently retcode is always UPAP_AUTHACK */
+ BZERO(rpasswd, rpasswdlen);
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0) {
+ UNTIMEOUT(upap_reqtimeout, u);
+ }
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(upap_state *u, u_char *inp, int id, int len)
+{
+ u_char msglen;
+ char *msg;
+
+ LWIP_UNUSED_ARG(id);
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n"));
+ return;
+ }
+
+ /*
+ * Parse message.
+ */
+ if (len < (int)sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n"));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n"));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nak.
+ */
+static void
+upap_rauthnak(upap_state *u, u_char *inp, int id, int len)
+{
+ u_char msglen;
+ char *msg;
+
+ LWIP_UNUSED_ARG(id);
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
+ return;
+ }
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n"));
+ } else {
+ GETCHAR(msglen, inp);
+ if(msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n"));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n"));
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(upap_state *u)
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char)
+ + u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf[u->us_unit];
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
+
+ UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id));
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen)
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf[u->us_unit];
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
+
+ UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate));
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static int upap_printpkt(
+ u_char *p,
+ int plen,
+ void (*printer) (void *, char *, ...),
+ void *arg
+)
+{
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(plen);
+ LWIP_UNUSED_ARG(printer);
+ LWIP_UNUSED_ARG(arg);
+ return 0;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* PAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/pap.h b/core/lwip/src/netif/ppp/pap.h
new file mode 100644
index 00000000..c99a2040
--- /dev/null
+++ b/core/lwip/src/netif/ppp/pap.h
@@ -0,0 +1,118 @@
+/*****************************************************************************
+* pap.h - PPP Password Authentication Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef PAP_H
+#define PAP_H
+
+#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ const char *us_user; /* User */
+ int us_userlen; /* User length */
+ const char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+extern upap_state upap[];
+
+void upap_authwithpeer (int, char *, char *);
+void upap_authpeer (int);
+
+extern struct protent pap_protent;
+
+#endif /* PAP_SUPPORT */
+
+#endif /* PAP_H */
diff --git a/core/lwip/src/netif/ppp/ppp.c b/core/lwip/src/netif/ppp/ppp.c
new file mode 100644
index 00000000..e9b433b0
--- /dev/null
+++ b/core/lwip/src/netif/ppp/ppp.c
@@ -0,0 +1,2020 @@
+/*****************************************************************************
+* ppp.c - Network Point to Point Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * if_pppvar.h - private structures and declarations for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip.h" /* for ip_input() */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "randm.h"
+#include "fsm.h"
+#if PAP_SUPPORT
+#include "pap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "chap.h"
+#endif /* CHAP_SUPPORT */
+#include "ipcp.h"
+#include "lcp.h"
+#include "magic.h"
+#include "auth.h"
+#if VJ_SUPPORT
+#include "vj.h"
+#endif /* VJ_SUPPORT */
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include "lwip/tcpip.h"
+#include "lwip/api.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback().
+ * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1.
+ * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded).
+ */
+#ifndef PPP_INPROC_MULTITHREADED
+#define PPP_INPROC_MULTITHREADED (NO_SYS==0)
+#endif
+
+/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session.
+ * Default is 0: call pppos_input() for received raw characters, charcater
+ * reception is up to the port */
+#ifndef PPP_INPROC_OWNTHREAD
+#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED
+#endif
+
+#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED
+ #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1"
+#endif
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_ADDRESS(p) (((u_char *)(p))[0])
+#define PPP_CONTROL(p) (((u_char *)(p))[1])
+#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/* PPP packet parser states. Current state indicates operation yet to be
+ * completed. */
+typedef enum {
+ PDIDLE = 0, /* Idle state - waiting. */
+ PDSTART, /* Process start flag. */
+ PDADDRESS, /* Process address field. */
+ PDCONTROL, /* Process control field. */
+ PDPROTOCOL1, /* Process protocol field 1. */
+ PDPROTOCOL2, /* Process protocol field 2. */
+ PDDATA /* Process data byte. */
+} PPPDevStates;
+
+#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07])
+
+/************************/
+/*** LOCAL DATA TYPES ***/
+/************************/
+
+/** RX buffer size: this may be configured smaller! */
+#ifndef PPPOS_RX_BUFSIZE
+#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN)
+#endif
+
+typedef struct PPPControlRx_s {
+ /** unit number / ppp descriptor */
+ int pd;
+ /** the rx file descriptor */
+ sio_fd_t fd;
+ /** receive buffer - encoded data is stored here */
+ u_char rxbuf[PPPOS_RX_BUFSIZE];
+
+ /* The input packet. */
+ struct pbuf *inHead, *inTail;
+
+#if PPPOS_SUPPORT
+ u16_t inProtocol; /* The input protocol code. */
+ u16_t inFCS; /* Input Frame Check Sequence value. */
+#endif /* PPPOS_SUPPORT */
+ PPPDevStates inState; /* The input process state. */
+ char inEscaped; /* Escape next character. */
+ ext_accm inACCM; /* Async-Ctl-Char-Map for input. */
+} PPPControlRx;
+
+/*
+ * PPP interface control block.
+ */
+typedef struct PPPControl_s {
+ PPPControlRx rx;
+ char openFlag; /* True when in use. */
+#if PPPOE_SUPPORT
+ struct netif *ethif;
+ struct pppoe_softc *pppoe_sc;
+#endif /* PPPOE_SUPPORT */
+ int if_up; /* True when the interface is up. */
+ int errCode; /* Code indicating why interface is down. */
+#if PPPOS_SUPPORT
+ sio_fd_t fd; /* File device ID of port. */
+#endif /* PPPOS_SUPPORT */
+ u16_t mtu; /* Peer's mru */
+ int pcomp; /* Does peer accept protocol compression? */
+ int accomp; /* Does peer accept addr/ctl compression? */
+ u_long lastXMit; /* Time of last transmission. */
+ ext_accm outACCM; /* Async-Ctl-Char-Map for output. */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ int vjEnabled; /* Flag indicating VJ compression enabled. */
+ struct vjcompress vjComp; /* Van Jacobson compression header. */
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+
+ struct netif netif;
+
+ struct ppp_addrs addrs;
+
+ void (*linkStatusCB)(void *ctx, int errCode, void *arg);
+ void *linkStatusCtx;
+
+} PPPControl;
+
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+ int protocol; /* PPP procotol, e.g. PPP_IP */
+ enum NPmode mode;
+};
+
+
+
+/***********************************/
+/*** LOCAL FUNCTION DECLARATIONS ***/
+/***********************************/
+#if PPPOS_SUPPORT
+#if PPP_INPROC_OWNTHREAD
+static void pppInputThread(void *arg);
+#endif /* PPP_INPROC_OWNTHREAD */
+static void pppDrop(PPPControlRx *pcrx);
+static void pppInProc(PPPControlRx *pcrx, u_char *s, int l);
+#endif /* PPPOS_SUPPORT */
+
+
+/******************************/
+/*** PUBLIC DATA STRUCTURES ***/
+/******************************/
+u_long subnetMask;
+
+static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *ppp_protocols[] = {
+ &lcp_protent,
+#if PAP_SUPPORT
+ &pap_protent,
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ &chap_protent,
+#endif /* CHAP_SUPPORT */
+#if CBCP_SUPPORT
+ &cbcp_protent,
+#endif /* CBCP_SUPPORT */
+ &ipcp_protent,
+#if CCP_SUPPORT
+ &ccp_protent,
+#endif /* CCP_SUPPORT */
+ NULL
+};
+
+
+/*
+ * Buffers for outgoing packets. This must be accessed only from the appropriate
+ * PPP task so that it doesn't need to be protected to avoid collisions.
+ */
+u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
+
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+
+#if PPPOS_SUPPORT
+/*
+ * FCS lookup table as calculated by genfcstab.
+ * @todo: smaller, slower implementation for lower memory footprint?
+ */
+static const u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/* PPP's Asynchronous-Control-Character-Map. The mask array is used
+ * to select the specific bit for a character. */
+static u_char pppACCMMask[] = {
+ 0x01,
+ 0x02,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80
+};
+
+/** Wake up the task blocked in reading from serial line (if any) */
+static void
+pppRecvWakeup(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd));
+ if (pppControl[pd].openFlag != 0) {
+ sio_read_abort(pppControl[pd].fd);
+ }
+}
+#endif /* PPPOS_SUPPORT */
+
+void
+pppLinkTerminated(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d\n", pd));
+
+#if PPPOE_SUPPORT
+ if (pppControl[pd].ethif) {
+ pppoe_disconnect(pppControl[pd].pppoe_sc);
+ } else
+#endif /* PPPOE_SUPPORT */
+ {
+#if PPPOS_SUPPORT
+ PPPControl* pc;
+ pppRecvWakeup(pd);
+ pc = &pppControl[pd];
+
+ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
+ }
+
+ pc->openFlag = 0;/**/
+#endif /* PPPOS_SUPPORT */
+ }
+ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: finished.\n"));
+}
+
+void
+pppLinkDown(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppLinkDown: unit %d\n", pd));
+
+#if PPPOE_SUPPORT
+ if (pppControl[pd].ethif) {
+ pppoe_disconnect(pppControl[pd].pppoe_sc);
+ } else
+#endif /* PPPOE_SUPPORT */
+ {
+#if PPPOS_SUPPORT
+ pppRecvWakeup(pd);
+#endif /* PPPOS_SUPPORT */
+ }
+}
+
+/** Initiate LCP open request */
+static void
+pppStart(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppStart: unit %d\n", pd));
+ lcp_lowerup(pd);
+ lcp_open(pd); /* Start protocol */
+ PPPDEBUG(LOG_DEBUG, ("pppStart: finished\n"));
+}
+
+/** LCP close request */
+static void
+pppStop(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppStop: unit %d\n", pd));
+ lcp_close(pd, "User request");
+}
+
+/** Called when carrier/link is lost */
+static void
+pppHup(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppHupCB: unit %d\n", pd));
+ lcp_lowerdown(pd);
+ link_terminated(pd);
+}
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/* Initialize the PPP subsystem. */
+
+struct ppp_settings ppp_settings;
+
+void
+pppInit(void)
+{
+ struct protent *protp;
+ int i, j;
+
+ memset(&ppp_settings, 0, sizeof(ppp_settings));
+ ppp_settings.usepeerdns = 1;
+ pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL);
+
+ magicInit();
+
+ subnetMask = PP_HTONL(0xffffff00UL);
+
+ for (i = 0; i < NUM_PPP; i++) {
+ /* Initialize each protocol to the standard option set. */
+ for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) {
+ (*protp->init)(i);
+ }
+ }
+}
+
+void
+pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd)
+{
+ switch(authType) {
+ case PPPAUTHTYPE_NONE:
+ default:
+#ifdef LWIP_PPP_STRICT_PAP_REJECT
+ ppp_settings.refuse_pap = 1;
+#else /* LWIP_PPP_STRICT_PAP_REJECT */
+ /* some providers request pap and accept an empty login/pw */
+ ppp_settings.refuse_pap = 0;
+#endif /* LWIP_PPP_STRICT_PAP_REJECT */
+ ppp_settings.refuse_chap = 1;
+ break;
+
+ case PPPAUTHTYPE_ANY:
+ /* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
+ * RFC 1994 says:
+ *
+ * In practice, within or associated with each PPP server, there is a
+ * database which associates "user" names with authentication
+ * information ("secrets"). It is not anticipated that a particular
+ * named user would be authenticated by multiple methods. This would
+ * make the user vulnerable to attacks which negotiate the least secure
+ * method from among a set (such as PAP rather than CHAP). If the same
+ * secret was used, PAP would reveal the secret to be used later with
+ * CHAP.
+ *
+ * Instead, for each user name there should be an indication of exactly
+ * one method used to authenticate that user name. If a user needs to
+ * make use of different authentication methods under different
+ * circumstances, then distinct user names SHOULD be employed, each of
+ * which identifies exactly one authentication method.
+ *
+ */
+ ppp_settings.refuse_pap = 0;
+ ppp_settings.refuse_chap = 0;
+ break;
+
+ case PPPAUTHTYPE_PAP:
+ ppp_settings.refuse_pap = 0;
+ ppp_settings.refuse_chap = 1;
+ break;
+
+ case PPPAUTHTYPE_CHAP:
+ ppp_settings.refuse_pap = 1;
+ ppp_settings.refuse_chap = 0;
+ break;
+ }
+
+ if(user) {
+ strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1);
+ ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0';
+ } else {
+ ppp_settings.user[0] = '\0';
+ }
+
+ if(passwd) {
+ strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1);
+ ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0';
+ } else {
+ ppp_settings.passwd[0] = '\0';
+ }
+}
+
+#if PPPOS_SUPPORT
+/** Open a new PPP connection using the given I/O device.
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session. If this port
+ * connects to a modem, the modem connection must be
+ * established before calling this.
+ * Return a new PPP connection descriptor on success or
+ * an error code (negative) on failure.
+ *
+ * pppOpen() is directly defined to this function.
+ */
+int
+pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx)
+{
+ PPPControl *pc;
+ int pd;
+
+ if (linkStatusCB == NULL) {
+ /* PPP is single-threaded: without a callback,
+ * there is no way to know when the link is up. */
+ return PPPERR_PARAM;
+ }
+
+ /* Find a free PPP session descriptor. */
+ for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++);
+
+ if (pd >= NUM_PPP) {
+ pd = PPPERR_OPEN;
+ } else {
+ pc = &pppControl[pd];
+ /* @todo: is this correct or do I overwrite something? */
+ memset(pc, 0, sizeof(PPPControl));
+ pc->rx.pd = pd;
+ pc->rx.fd = fd;
+
+ pc->openFlag = 1;
+ pc->fd = fd;
+
+#if VJ_SUPPORT
+ vj_compress_init(&pc->vjComp);
+#endif /* VJ_SUPPORT */
+
+ /*
+ * Default the in and out accm so that escape and flag characters
+ * are always escaped.
+ */
+ pc->rx.inACCM[15] = 0x60; /* no need to protect since RX is not running */
+ pc->outACCM[15] = 0x60;
+
+ pc->linkStatusCB = linkStatusCB;
+ pc->linkStatusCtx = linkStatusCtx;
+
+ /*
+ * Start the connection and handle incoming events (packet or timeout).
+ */
+ PPPDEBUG(LOG_INFO, ("pppOverSerialOpen: unit %d: Connecting\n", pd));
+ pppStart(pd);
+#if PPP_INPROC_OWNTHREAD
+ sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO);
+#endif
+ }
+
+ return pd;
+}
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+static void pppOverEthernetLinkStatusCB(int pd, int up);
+
+void
+pppOverEthernetClose(int pd)
+{
+ PPPControl* pc = &pppControl[pd];
+
+ /* *TJL* There's no lcp_deinit */
+ lcp_close(pd, NULL);
+
+ pppoe_destroy(&pc->netif);
+}
+
+int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx)
+{
+ PPPControl *pc;
+ int pd;
+
+ LWIP_UNUSED_ARG(service_name);
+ LWIP_UNUSED_ARG(concentrator_name);
+
+ if (linkStatusCB == NULL) {
+ /* PPP is single-threaded: without a callback,
+ * there is no way to know when the link is up. */
+ return PPPERR_PARAM;
+ }
+
+ /* Find a free PPP session descriptor. Critical region? */
+ for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++);
+ if (pd >= NUM_PPP) {
+ pd = PPPERR_OPEN;
+ } else {
+ pc = &pppControl[pd];
+ memset(pc, 0, sizeof(PPPControl));
+ pc->openFlag = 1;
+ pc->ethif = ethif;
+
+ pc->linkStatusCB = linkStatusCB;
+ pc->linkStatusCtx = linkStatusCtx;
+
+ lcp_wantoptions[pd].mru = PPPOE_MAXMTU;
+ lcp_wantoptions[pd].neg_asyncmap = 0;
+ lcp_wantoptions[pd].neg_pcompression = 0;
+ lcp_wantoptions[pd].neg_accompression = 0;
+
+ lcp_allowoptions[pd].mru = PPPOE_MAXMTU;
+ lcp_allowoptions[pd].neg_asyncmap = 0;
+ lcp_allowoptions[pd].neg_pcompression = 0;
+ lcp_allowoptions[pd].neg_accompression = 0;
+
+ if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) {
+ pc->openFlag = 0;
+ return PPPERR_OPEN;
+ }
+
+ pppoe_connect(pc->pppoe_sc);
+ }
+
+ return pd;
+}
+#endif /* PPPOE_SUPPORT */
+
+
+/* Close a PPP connection and release the descriptor.
+ * Any outstanding packets in the queues are dropped.
+ * Return 0 on success, an error code on failure. */
+int
+pppClose(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 0;
+
+ PPPDEBUG(LOG_DEBUG, ("pppClose() called\n"));
+
+ /* Disconnect */
+#if PPPOE_SUPPORT
+ if(pc->ethif) {
+ PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd));
+ pc->errCode = PPPERR_USER;
+ /* This will leave us at PHASE_DEAD. */
+ pppStop(pd);
+ } else
+#endif /* PPPOE_SUPPORT */
+ {
+#if PPPOS_SUPPORT
+ PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd));
+ pc->errCode = PPPERR_USER;
+ /* This will leave us at PHASE_DEAD. */
+ pppStop(pd);
+ pppRecvWakeup(pd);
+#endif /* PPPOS_SUPPORT */
+ }
+
+ return st;
+}
+
+/* This function is called when carrier is lost on the PPP channel. */
+void
+pppSigHUP(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd));
+ pppHup(pd);
+}
+
+#if PPPOS_SUPPORT
+static void
+nPut(PPPControl *pc, struct pbuf *nb)
+{
+ struct pbuf *b;
+ int c;
+
+ for(b = nb; b != NULL; b = b->next) {
+ if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) {
+ PPPDEBUG(LOG_WARNING,
+ ("PPP nPut: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pc->fd, b->len, c, c));
+ LINK_STATS_INC(link.err);
+ pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */
+ snmp_inc_ifoutdiscards(&pc->netif);
+ pbuf_free(nb);
+ return;
+ }
+ }
+
+ snmp_add_ifoutoctets(&pc->netif, nb->tot_len);
+ snmp_inc_ifoutucastpkts(&pc->netif);
+ pbuf_free(nb);
+ LINK_STATS_INC(link.xmit);
+}
+
+/*
+ * pppAppend - append given character to end of given pbuf. If outACCM
+ * is not NULL and the character needs to be escaped, do so.
+ * If pbuf is full, append another.
+ * Return the current pbuf.
+ */
+static struct pbuf *
+pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM)
+{
+ struct pbuf *tb = nb;
+
+ /* Make sure there is room for the character and an escape code.
+ * Sure we don't quite fill the buffer if the character doesn't
+ * get escaped but is one character worth complicating this? */
+ /* Note: We assume no packet header. */
+ if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) {
+ tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (tb) {
+ nb->next = tb;
+ } else {
+ LINK_STATS_INC(link.memerr);
+ }
+ nb = tb;
+ }
+
+ if (nb) {
+ if (outACCM && ESCAPE_P(*outACCM, c)) {
+ *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE;
+ *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS;
+ } else {
+ *((u_char*)nb->payload + nb->len++) = c;
+ }
+ }
+
+ return tb;
+}
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+static err_t
+pppifOutputOverEthernet(int pd, struct pbuf *p)
+{
+ PPPControl *pc = &pppControl[pd];
+ struct pbuf *pb;
+ u_short protocol = PPP_IP;
+ int i=0;
+ u16_t tot_len;
+
+ /* @todo: try to use pbuf_header() here! */
+ pb = pbuf_alloc(PBUF_LINK, PPPOE_HDRLEN + sizeof(protocol), PBUF_RAM);
+ if(!pb) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return ERR_MEM;
+ }
+
+ pbuf_header(pb, -(s16_t)PPPOE_HDRLEN);
+
+ pc->lastXMit = sys_jiffies();
+
+ if (!pc->pcomp || protocol > 0xFF) {
+ *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF;
+ }
+ *((u_char*)pb->payload + i) = protocol & 0xFF;
+
+ pbuf_chain(pb, p);
+ tot_len = pb->tot_len;
+
+ if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_DEVICE;
+ }
+
+ snmp_add_ifoutoctets(&pc->netif, tot_len);
+ snmp_inc_ifoutucastpkts(&pc->netif);
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+#endif /* PPPOE_SUPPORT */
+
+/* Send a packet on the given connection. */
+static err_t
+pppifOutput(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr)
+{
+ int pd = (int)(size_t)netif->state;
+ PPPControl *pc = &pppControl[pd];
+#if PPPOS_SUPPORT
+ u_short protocol = PPP_IP;
+ u_int fcsOut = PPP_INITFCS;
+ struct pbuf *headMB = NULL, *tailMB = NULL, *p;
+ u_char c;
+#endif /* PPPOS_SUPPORT */
+
+ LWIP_UNUSED_ARG(ipaddr);
+
+ /* Validate parameters. */
+ /* We let any protocol value go through - it can't hurt us
+ * and the peer will just drop it if it's not accepting it. */
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) {
+ PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad parms prot=%d pb=%p\n",
+ pd, PPP_IP, pb));
+ LINK_STATS_INC(link.opterr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_ARG;
+ }
+
+ /* Check that the link is up. */
+ if (lcp_phase[pd] == PHASE_DEAD) {
+ PPPDEBUG(LOG_ERR, ("pppifOutput[%d]: link not up\n", pd));
+ LINK_STATS_INC(link.rterr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_RTE;
+ }
+
+#if PPPOE_SUPPORT
+ if(pc->ethif) {
+ return pppifOutputOverEthernet(pd, pb);
+ }
+#endif /* PPPOE_SUPPORT */
+
+#if PPPOS_SUPPORT
+ /* Grab an output buffer. */
+ headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (headMB == NULL) {
+ PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: first alloc fail\n", pd));
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_MEM;
+ }
+
+#if VJ_SUPPORT
+ /*
+ * Attempt Van Jacobson header compression if VJ is configured and
+ * this is an IP packet.
+ */
+ if (protocol == PPP_IP && pc->vjEnabled) {
+ switch (vj_compress_tcp(&pc->vjComp, pb)) {
+ case TYPE_IP:
+ /* No change...
+ protocol = PPP_IP_PROTOCOL; */
+ break;
+ case TYPE_COMPRESSED_TCP:
+ protocol = PPP_VJC_COMP;
+ break;
+ case TYPE_UNCOMPRESSED_TCP:
+ protocol = PPP_VJC_UNCOMP;
+ break;
+ default:
+ PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad IP packet\n", pd));
+ LINK_STATS_INC(link.proterr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ pbuf_free(headMB);
+ return ERR_VAL;
+ }
+ }
+#endif /* VJ_SUPPORT */
+
+ tailMB = headMB;
+
+ /* Build the PPP header. */
+ if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) {
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+ }
+
+ pc->lastXMit = sys_jiffies();
+ if (!pc->accomp) {
+ fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS);
+ tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM);
+ fcsOut = PPP_FCS(fcsOut, PPP_UI);
+ tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM);
+ }
+ if (!pc->pcomp || protocol > 0xFF) {
+ c = (protocol >> 8) & 0xFF;
+ fcsOut = PPP_FCS(fcsOut, c);
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ }
+ c = protocol & 0xFF;
+ fcsOut = PPP_FCS(fcsOut, c);
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+
+ /* Load packet. */
+ for(p = pb; p; p = p->next) {
+ int n;
+ u_char *sPtr;
+
+ sPtr = (u_char*)p->payload;
+ n = p->len;
+ while (n-- > 0) {
+ c = *sPtr++;
+
+ /* Update FCS before checking for special characters. */
+ fcsOut = PPP_FCS(fcsOut, c);
+
+ /* Copy to output buffer escaping special characters. */
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ }
+ }
+
+ /* Add FCS and trailing flag. */
+ c = ~fcsOut & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ c = (~fcsOut >> 8) & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+
+ /* If we failed to complete the packet, throw it away. */
+ if (!tailMB) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppifOutput[%d]: Alloc err - dropping proto=%d\n",
+ pd, protocol));
+ pbuf_free(headMB);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_MEM;
+ }
+
+ /* Send it. */
+ PPPDEBUG(LOG_INFO, ("pppifOutput[%d]: proto=0x%"X16_F"\n", pd, protocol));
+
+ nPut(pc, headMB);
+#endif /* PPPOS_SUPPORT */
+
+ return ERR_OK;
+}
+
+/* Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure. */
+int
+pppIOCtl(int pd, int cmd, void *arg)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 0;
+
+ if (pd < 0 || pd >= NUM_PPP) {
+ st = PPPERR_PARAM;
+ } else {
+ switch(cmd) {
+ case PPPCTLG_UPSTATUS: /* Get the PPP up status. */
+ if (arg) {
+ *(int *)arg = (int)(pc->if_up);
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+ case PPPCTLS_ERRCODE: /* Set the PPP error code. */
+ if (arg) {
+ pc->errCode = *(int *)arg;
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+ case PPPCTLG_ERRCODE: /* Get the PPP error code. */
+ if (arg) {
+ *(int *)arg = (int)(pc->errCode);
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+#if PPPOS_SUPPORT
+ case PPPCTLG_FD: /* Get the fd associated with the ppp */
+ if (arg) {
+ *(sio_fd_t *)arg = pc->fd;
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+#endif /* PPPOS_SUPPORT */
+ default:
+ st = PPPERR_PARAM;
+ break;
+ }
+ }
+
+ return st;
+}
+
+/*
+ * Return the Maximum Transmission Unit for the given PPP connection.
+ */
+u_short
+pppMTU(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ u_short st;
+
+ /* Validate parameters. */
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ } else {
+ st = pc->mtu;
+ }
+
+ return st;
+}
+
+#if PPPOE_SUPPORT
+int
+pppWriteOverEthernet(int pd, const u_char *s, int n)
+{
+ PPPControl *pc = &pppControl[pd];
+ struct pbuf *pb;
+
+ /* skip address & flags */
+ s += 2;
+ n -= 2;
+
+ LWIP_ASSERT("PPPOE_HDRLEN + n <= 0xffff", PPPOE_HDRLEN + n <= 0xffff);
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HDRLEN + n), PBUF_RAM);
+ if(!pb) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_ALLOC;
+ }
+
+ pbuf_header(pb, -(s16_t)PPPOE_HDRLEN);
+
+ pc->lastXMit = sys_jiffies();
+
+ MEMCPY(pb->payload, s, n);
+
+ if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_DEVICE;
+ }
+
+ snmp_add_ifoutoctets(&pc->netif, (u16_t)n);
+ snmp_inc_ifoutucastpkts(&pc->netif);
+ LINK_STATS_INC(link.xmit);
+ return PPPERR_NONE;
+}
+#endif /* PPPOE_SUPPORT */
+
+/*
+ * Write n characters to a ppp link.
+ * RETURN: >= 0 Number of characters written
+ * -1 Failed to write to device
+ */
+int
+pppWrite(int pd, const u_char *s, int n)
+{
+ PPPControl *pc = &pppControl[pd];
+#if PPPOS_SUPPORT
+ u_char c;
+ u_int fcsOut;
+ struct pbuf *headMB, *tailMB;
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+ if(pc->ethif) {
+ return pppWriteOverEthernet(pd, s, n);
+ }
+#endif /* PPPOE_SUPPORT */
+
+#if PPPOS_SUPPORT
+ headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (headMB == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_ALLOC;
+ }
+
+ tailMB = headMB;
+
+ /* If the link has been idle, we'll send a fresh flag character to
+ * flush any noise. */
+ if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) {
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+ }
+ pc->lastXMit = sys_jiffies();
+
+ fcsOut = PPP_INITFCS;
+ /* Load output buffer. */
+ while (n-- > 0) {
+ c = *s++;
+
+ /* Update FCS before checking for special characters. */
+ fcsOut = PPP_FCS(fcsOut, c);
+
+ /* Copy to output buffer escaping special characters. */
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ }
+
+ /* Add FCS and trailing flag. */
+ c = ~fcsOut & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ c = (~fcsOut >> 8) & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+
+ /* If we failed to complete the packet, throw it away.
+ * Otherwise send it. */
+ if (!tailMB) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len));
+ /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */
+ pbuf_free(headMB);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_ALLOC;
+ }
+
+ PPPDEBUG(LOG_INFO, ("pppWrite[%d]: len=%d\n", pd, headMB->len));
+ /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */
+ nPut(pc, headMB);
+#endif /* PPPOS_SUPPORT */
+
+ return PPPERR_NONE;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config( int unit, u16_t mtu, u32_t asyncmap, int pcomp, int accomp)
+{
+ PPPControl *pc = &pppControl[unit];
+ int i;
+
+ pc->mtu = mtu;
+ pc->pcomp = pcomp;
+ pc->accomp = accomp;
+
+ /* Load the ACCM bits for the 32 control codes. */
+ for (i = 0; i < 32/8; i++) {
+ pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF);
+ }
+ PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: outACCM=%X %X %X %X\n",
+ unit,
+ pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3]));
+}
+
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(int unit, ext_accm *accm)
+{
+ SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm));
+ PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n",
+ unit,
+ pppControl[unit].outACCM[0],
+ pppControl[unit].outACCM[1],
+ pppControl[unit].outACCM[2],
+ pppControl[unit].outACCM[3]));
+}
+
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp)
+{
+ PPPControl *pc = &pppControl[unit];
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(accomp);
+ LWIP_UNUSED_ARG(pcomp);
+ LWIP_UNUSED_ARG(mru);
+
+ /* Load the ACCM bits for the 32 control codes. */
+ SYS_ARCH_PROTECT(lev);
+ for (i = 0; i < 32 / 8; i++) {
+ /* @todo: does this work? ext_accm has been modified from pppd! */
+ pc->rx.inACCM[i] = (u_char)(asyncmap >> (i * 8));
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: inACCM=%X %X %X %X\n",
+ unit,
+ pc->rx.inACCM[0], pc->rx.inACCM[1], pc->rx.inACCM[2], pc->rx.inACCM[3]));
+}
+
+#if 0
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use. Returns 1 if the method and parameters
+ * are OK, 0 if the method is known but the parameters are not OK
+ * (e.g. code size should be reduced), or -1 if the method is unknown.
+ */
+int
+ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr)
+{
+ return 0; /* XXX Currently no compression. */
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(int unit, int isopen, int isup)
+{
+ /* XXX */
+}
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(int unit)
+{
+ /* XXX */
+ return 0;
+}
+#endif
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(int u, struct ppp_idle *ip)
+{
+ /* XXX */
+ LWIP_UNUSED_ARG(u);
+ LWIP_UNUSED_ARG(ip);
+
+ return 0;
+}
+
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u32_t
+GetMask(u32_t addr)
+{
+ u32_t mask, nmask;
+
+ htonl(addr);
+ if (IP_CLASSA(addr)) { /* determine network mask for address class */
+ nmask = IP_CLASSA_NET;
+ } else if (IP_CLASSB(addr)) {
+ nmask = IP_CLASSB_NET;
+ } else {
+ nmask = IP_CLASSC_NET;
+ }
+
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = subnetMask | htonl(nmask);
+
+ /* XXX
+ * Scan through the system's network interfaces.
+ * Get each netmask and OR them into our mask.
+ */
+
+ return mask;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid)
+{
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPControl *pc = &pppControl[pd];
+
+ pc->vjEnabled = vjcomp;
+ pc->vjComp.compressSlot = cidcomp;
+ pc->vjComp.maxSlotIndex = maxcid;
+ PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n",
+ vjcomp, cidcomp, maxcid));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ LWIP_UNUSED_ARG(pd);
+ LWIP_UNUSED_ARG(vjcomp);
+ LWIP_UNUSED_ARG(cidcomp);
+ LWIP_UNUSED_ARG(maxcid);
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+
+ return 0;
+}
+
+/*
+ * pppifNetifInit - netif init callback
+ */
+static err_t
+pppifNetifInit(struct netif *netif)
+{
+ netif->name[0] = 'p';
+ netif->name[1] = 'p';
+ netif->output = pppifOutput;
+ netif->mtu = pppMTU((int)(size_t)netif->state);
+ netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP;
+#if LWIP_NETIF_HOSTNAME
+ /* @todo: Initialize interface hostname */
+ /* netif_set_hostname(netif, "lwip"); */
+#endif /* LWIP_NETIF_HOSTNAME */
+ return ERR_OK;
+}
+
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_remove(&pc->netif);
+ if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask,
+ &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) {
+ netif_set_up(&pc->netif);
+ pc->if_up = 1;
+ pc->errCode = PPPERR_NONE;
+
+ PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs);
+ }
+ } else {
+ st = 0;
+ PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd));
+ }
+ }
+
+ return st;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(int u, int proto, enum NPmode mode)
+{
+ LWIP_UNUSED_ARG(u);
+ LWIP_UNUSED_ARG(proto);
+ LWIP_UNUSED_ARG(mode);
+ return 0;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd));
+ } else {
+ pc->if_up = 0;
+ /* make sure the netif status callback is called */
+ netif_set_down(&pc->netif);
+ netif_remove(&pc->netif);
+ PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL);
+ }
+ }
+ return st;
+}
+
+/**
+ * sifaddr - Config the interface IP addresses and netmask.
+ * @param pd Interface unit ???
+ * @param o Our IP address ???
+ * @param h His IP address ???
+ * @param m IP subnet mask ???
+ * @param ns1 Primary DNS
+ * @param ns2 Secondary DNS
+ */
+int
+sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o));
+ SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h));
+ SMEMCPY(&pc->addrs.netmask, &m, sizeof(m));
+ SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1));
+ SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2));
+ }
+ return st;
+}
+
+/**
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ * @param pd Interface unit ???
+ * @param o Our IP address ???
+ * @param h IP broadcast address ???
+ */
+int
+cifaddr( int pd, u32_t o, u32_t h)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(o);
+ LWIP_UNUSED_ARG(h);
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.netmask, 255,255,255,0);
+ IP4_ADDR(&pc->addrs.dns1, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.dns2, 0,0,0,0);
+ }
+ return st;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(int pd, u32_t l, u32_t g)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(l);
+ LWIP_UNUSED_ARG(g);
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_set_default(&pc->netif);
+ }
+
+ /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */
+
+ return st;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(int pd, u32_t l, u32_t g)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(l);
+ LWIP_UNUSED_ARG(g);
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_set_default(NULL);
+ }
+
+ return st;
+}
+
+/**********************************/
+/*** LOCAL FUNCTION DEFINITIONS ***/
+/**********************************/
+
+#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD
+/* The main PPP process function. This implements the state machine according
+ * to section 4 of RFC 1661: The Point-To-Point Protocol. */
+static void
+pppInputThread(void *arg)
+{
+ int count;
+ PPPControlRx *pcrx = arg;
+
+ while (lcp_phase[pcrx->pd] != PHASE_DEAD) {
+ count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE);
+ if(count > 0) {
+ pppInProc(pcrx, pcrx->rxbuf, count);
+ } else {
+ /* nothing received, give other tasks a chance to run */
+ sys_msleep(1);
+ }
+ }
+}
+#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */
+
+#if PPPOE_SUPPORT
+
+void
+pppOverEthernetInitFailed(int pd)
+{
+ PPPControl* pc;
+
+ pppHup(pd);
+ pppStop(pd);
+
+ pc = &pppControl[pd];
+ pppoe_destroy(&pc->netif);
+ pc->openFlag = 0;
+
+ if(pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
+ }
+}
+
+static void
+pppOverEthernetLinkStatusCB(int pd, int up)
+{
+ if(up) {
+ PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd));
+ pppStart(pd);
+ } else {
+ pppOverEthernetInitFailed(pd);
+ }
+}
+#endif /* PPPOE_SUPPORT */
+
+struct pbuf *
+pppSingleBuf(struct pbuf *p)
+{
+ struct pbuf *q, *b;
+ u_char *pl;
+
+ if(p->tot_len == p->len) {
+ return p;
+ }
+
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(!q) {
+ PPPDEBUG(LOG_ERR,
+ ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len));
+ return p; /* live dangerously */
+ }
+
+ for(b = p, pl = q->payload; b != NULL; b = b->next) {
+ MEMCPY(pl, b->payload, b->len);
+ pl += b->len;
+ }
+
+ pbuf_free(p);
+
+ return q;
+}
+
+struct pppInputHeader {
+ int unit;
+ u16_t proto;
+};
+
+/*
+ * Pass the processed input packet to the appropriate handler.
+ * This function and all handlers run in the context of the tcpip_thread
+ */
+static void
+pppInput(void *arg)
+{
+ struct pbuf *nb = (struct pbuf *)arg;
+ u16_t protocol;
+ int pd;
+
+ pd = ((struct pppInputHeader *)nb->payload)->unit;
+ protocol = ((struct pppInputHeader *)nb->payload)->proto;
+
+ if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+
+ LINK_STATS_INC(link.recv);
+ snmp_inc_ifinucastpkts(&pppControl[pd].netif);
+ snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len);
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) {
+ if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) ||
+ (lcp_phase[pd] != PHASE_AUTHENTICATE)) {
+ PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd]));
+ goto drop;
+ }
+ }
+
+ switch(protocol) {
+ case PPP_VJC_COMP: /* VJ compressed TCP */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len));
+ /*
+ * Clip off the VJ header and prepend the rebuilt TCP/IP header and
+ * pass the result to IP.
+ */
+ if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ /* No handler for this protocol so drop the packet. */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload));
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+ break;
+
+ case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len));
+ /*
+ * Process the TCP/IP header for VJ header compression and then pass
+ * the packet to IP.
+ */
+ if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ /* No handler for this protocol so drop the packet. */
+ PPPDEBUG(LOG_INFO,
+ ("pppInput[%d]: drop VJ UnComp in %d:.*H\n",
+ pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload));
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+ break;
+
+ case PPP_IP: /* Internet Protocol */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len));
+ if (pppControl[pd].netif.input) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ break;
+
+ default: {
+ struct protent *protp;
+ int i;
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len));
+ nb = pppSingleBuf(nb);
+ (*protp->input)(pd, nb->payload, nb->len);
+ PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd));
+ goto out;
+ }
+ }
+
+ /* No handler for this protocol so reject the packet. */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len));
+ if (pbuf_header(nb, sizeof(protocol))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+#if BYTE_ORDER == LITTLE_ENDIAN
+ protocol = htons(protocol);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ SMEMCPY(nb->payload, &protocol, sizeof(protocol));
+ lcp_sprotrej(pd, nb->payload, nb->len);
+ }
+ break;
+ }
+
+drop:
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pd].netif);
+
+out:
+ pbuf_free(nb);
+ return;
+}
+
+#if PPPOS_SUPPORT
+/*
+ * Drop the input packet.
+ */
+static void
+pppDrop(PPPControlRx *pcrx)
+{
+ if (pcrx->inHead != NULL) {
+#if 0
+ PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload));
+#endif
+ PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead));
+ if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) {
+ pbuf_free(pcrx->inTail);
+ }
+ pbuf_free(pcrx->inHead);
+ pcrx->inHead = NULL;
+ pcrx->inTail = NULL;
+ }
+#if VJ_SUPPORT
+ vj_uncompress_err(&pppControl[pcrx->pd].vjComp);
+#endif /* VJ_SUPPORT */
+
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif);
+}
+
+/** Pass received raw characters to PPPoS to be decoded. This function is
+ * thread-safe and can be called from a dedicated RX-thread or from a main-loop.
+ *
+ * @param pd PPP descriptor index, returned by pppOpen()
+ * @param data received data
+ * @param len length of received data
+ */
+void
+pppos_input(int pd, u_char* data, int len)
+{
+ pppInProc(&pppControl[pd].rx, data, len);
+}
+
+/**
+ * Process a received octet string.
+ */
+static void
+pppInProc(PPPControlRx *pcrx, u_char *s, int l)
+{
+ struct pbuf *nextNBuf;
+ u_char curChar;
+ u_char escaped;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ PPPDEBUG(LOG_DEBUG, ("pppInProc[%d]: got %d bytes\n", pcrx->pd, l));
+ while (l-- > 0) {
+ curChar = *s++;
+
+ SYS_ARCH_PROTECT(lev);
+ escaped = ESCAPE_P(pcrx->inACCM, curChar);
+ SYS_ARCH_UNPROTECT(lev);
+ /* Handle special characters. */
+ if (escaped) {
+ /* Check for escape sequences. */
+ /* XXX Note that this does not handle an escaped 0x5d character which
+ * would appear as an escape character. Since this is an ASCII ']'
+ * and there is no reason that I know of to escape it, I won't complicate
+ * the code to handle this case. GLL */
+ if (curChar == PPP_ESCAPE) {
+ pcrx->inEscaped = 1;
+ /* Check for the flag character. */
+ } else if (curChar == PPP_FLAG) {
+ /* If this is just an extra flag character, ignore it. */
+ if (pcrx->inState <= PDADDRESS) {
+ /* ignore it */;
+ /* If we haven't received the packet header, drop what has come in. */
+ } else if (pcrx->inState < PDDATA) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppInProc[%d]: Dropping incomplete packet %d\n",
+ pcrx->pd, pcrx->inState));
+ LINK_STATS_INC(link.lenerr);
+ pppDrop(pcrx);
+ /* If the fcs is invalid, drop the packet. */
+ } else if (pcrx->inFCS != PPP_GOODFCS) {
+ PPPDEBUG(LOG_INFO,
+ ("pppInProc[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n",
+ pcrx->pd, pcrx->inFCS, pcrx->inProtocol));
+ /* Note: If you get lots of these, check for UART frame errors or try different baud rate */
+ LINK_STATS_INC(link.chkerr);
+ pppDrop(pcrx);
+ /* Otherwise it's a good packet so pass it on. */
+ } else {
+ struct pbuf *inp;
+ /* Trim off the checksum. */
+ if(pcrx->inTail->len >= 2) {
+ pcrx->inTail->len -= 2;
+
+ pcrx->inTail->tot_len = pcrx->inTail->len;
+ if (pcrx->inTail != pcrx->inHead) {
+ pbuf_cat(pcrx->inHead, pcrx->inTail);
+ }
+ } else {
+ pcrx->inTail->tot_len = pcrx->inTail->len;
+ if (pcrx->inTail != pcrx->inHead) {
+ pbuf_cat(pcrx->inHead, pcrx->inTail);
+ }
+
+ pbuf_realloc(pcrx->inHead, pcrx->inHead->tot_len - 2);
+ }
+
+ /* Dispatch the packet thereby consuming it. */
+ inp = pcrx->inHead;
+ /* Packet consumed, release our references. */
+ pcrx->inHead = NULL;
+ pcrx->inTail = NULL;
+#if PPP_INPROC_MULTITHREADED
+ if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) {
+ PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd));
+ pbuf_free(inp);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif);
+ }
+#else /* PPP_INPROC_MULTITHREADED */
+ pppInput(inp);
+#endif /* PPP_INPROC_MULTITHREADED */
+ }
+
+ /* Prepare for a new packet. */
+ pcrx->inFCS = PPP_INITFCS;
+ pcrx->inState = PDADDRESS;
+ pcrx->inEscaped = 0;
+ /* Other characters are usually control characters that may have
+ * been inserted by the physical layer so here we just drop them. */
+ } else {
+ PPPDEBUG(LOG_WARNING,
+ ("pppInProc[%d]: Dropping ACCM char <%d>\n", pcrx->pd, curChar));
+ }
+ /* Process other characters. */
+ } else {
+ /* Unencode escaped characters. */
+ if (pcrx->inEscaped) {
+ pcrx->inEscaped = 0;
+ curChar ^= PPP_TRANS;
+ }
+
+ /* Process character relative to current state. */
+ switch(pcrx->inState) {
+ case PDIDLE: /* Idle state - waiting. */
+ /* Drop the character if it's not 0xff
+ * we would have processed a flag character above. */
+ if (curChar != PPP_ALLSTATIONS) {
+ break;
+ }
+
+ /* Fall through */
+ case PDSTART: /* Process start flag. */
+ /* Prepare for a new packet. */
+ pcrx->inFCS = PPP_INITFCS;
+
+ /* Fall through */
+ case PDADDRESS: /* Process address field. */
+ if (curChar == PPP_ALLSTATIONS) {
+ pcrx->inState = PDCONTROL;
+ break;
+ }
+ /* Else assume compressed address and control fields so
+ * fall through to get the protocol... */
+ case PDCONTROL: /* Process control field. */
+ /* If we don't get a valid control code, restart. */
+ if (curChar == PPP_UI) {
+ pcrx->inState = PDPROTOCOL1;
+ break;
+ }
+#if 0
+ else {
+ PPPDEBUG(LOG_WARNING,
+ ("pppInProc[%d]: Invalid control <%d>\n", pcrx->pd, curChar));
+ pcrx->inState = PDSTART;
+ }
+#endif
+ case PDPROTOCOL1: /* Process protocol field 1. */
+ /* If the lower bit is set, this is the end of the protocol
+ * field. */
+ if (curChar & 1) {
+ pcrx->inProtocol = curChar;
+ pcrx->inState = PDDATA;
+ } else {
+ pcrx->inProtocol = (u_int)curChar << 8;
+ pcrx->inState = PDPROTOCOL2;
+ }
+ break;
+ case PDPROTOCOL2: /* Process protocol field 2. */
+ pcrx->inProtocol |= curChar;
+ pcrx->inState = PDDATA;
+ break;
+ case PDDATA: /* Process data byte. */
+ /* Make space to receive processed data. */
+ if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) {
+ if (pcrx->inTail != NULL) {
+ pcrx->inTail->tot_len = pcrx->inTail->len;
+ if (pcrx->inTail != pcrx->inHead) {
+ pbuf_cat(pcrx->inHead, pcrx->inTail);
+ /* give up the inTail reference now */
+ pcrx->inTail = NULL;
+ }
+ }
+ /* If we haven't started a packet, we need a packet header. */
+ nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (nextNBuf == NULL) {
+ /* No free buffers. Drop the input packet and let the
+ * higher layers deal with it. Continue processing
+ * the received pbuf chain in case a new packet starts. */
+ PPPDEBUG(LOG_ERR, ("pppInProc[%d]: NO FREE MBUFS!\n", pcrx->pd));
+ LINK_STATS_INC(link.memerr);
+ pppDrop(pcrx);
+ pcrx->inState = PDSTART; /* Wait for flag sequence. */
+ break;
+ }
+ if (pcrx->inHead == NULL) {
+ struct pppInputHeader *pih = nextNBuf->payload;
+
+ pih->unit = pcrx->pd;
+ pih->proto = pcrx->inProtocol;
+
+ nextNBuf->len += sizeof(*pih);
+
+ pcrx->inHead = nextNBuf;
+ }
+ pcrx->inTail = nextNBuf;
+ }
+ /* Load character into buffer. */
+ ((u_char*)pcrx->inTail->payload)[pcrx->inTail->len++] = curChar;
+ break;
+ }
+
+ /* update the frame check sequence number. */
+ pcrx->inFCS = PPP_FCS(pcrx->inFCS, curChar);
+ }
+ } /* while (l-- > 0), all bytes processed */
+
+ avRandomize();
+}
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+void
+pppInProcOverEthernet(int pd, struct pbuf *pb)
+{
+ struct pppInputHeader *pih;
+ u16_t inProtocol;
+
+ if(pb->len < sizeof(inProtocol)) {
+ PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: too small for protocol field\n"));
+ goto drop;
+ }
+
+ inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1];
+
+ /* make room for pppInputHeader - should not fail */
+ if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) {
+ PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: could not allocate room for header\n"));
+ goto drop;
+ }
+
+ pih = pb->payload;
+
+ pih->unit = pd;
+ pih->proto = inProtocol;
+
+ /* Dispatch the packet thereby consuming it. */
+ pppInput(pb);
+ return;
+
+drop:
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pd].netif);
+ pbuf_free(pb);
+ return;
+}
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/** Set the status callback of a PPP's netif
+ *
+ * @param pd The PPP descriptor returned by pppOpen()
+ * @param status_callback pointer to the status callback function
+ *
+ * @see netif_set_status_callback
+ */
+void
+ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback)
+{
+ netif_set_status_callback(&pppControl[pd].netif, status_callback);
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+/** Set the link callback of a PPP's netif
+ *
+ * @param pd The PPP descriptor returned by pppOpen()
+ * @param link_callback pointer to the link callback function
+ *
+ * @see netif_set_link_callback
+ */
+void
+ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback)
+{
+ netif_set_link_callback(&pppControl[pd].netif, link_callback);
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/ppp.h b/core/lwip/src/netif/ppp/ppp.h
new file mode 100644
index 00000000..a72ac957
--- /dev/null
+++ b/core/lwip/src/netif/ppp/ppp.h
@@ -0,0 +1,483 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+
+#ifndef PPP_H
+#define PPP_H
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/sio.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+
+/** Some defines for code we skip compared to the original pppd.
+ * These are just here to minimise the use of the ugly "#if 0". */
+#define PPP_ADDITIONAL_CALLBACKS 0
+
+/** Some error checks to test for unsupported code */
+#if CBCP_SUPPORT
+#error "CBCP is not supported in lwIP PPP"
+#endif
+#if CCP_SUPPORT
+#error "CCP is not supported in lwIP PPP"
+#endif
+
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0)
+#define UNTIMEOUT(f, a) sys_untimeout((f), (a))
+
+
+#ifndef __u_char_defined
+
+/* Type definitions for BSD code. */
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef unsigned char u_char;
+
+#endif
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, and numerous additions.
+ */
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN 4 /* octets for standard ppp header */
+#define PPP_FCSLEN 2 /* octets for FCS */
+
+
+/*
+ * Significant octet values.
+ */
+#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
+#define PPP_UI 0x03 /* Unnumbered Information */
+#define PPP_FLAG 0x7e /* Flag Sequence */
+#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */
+#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP 0x21 /* Internet Protocol */
+#define PPP_AT 0x29 /* AppleTalk Protocol */
+#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
+#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#define PPP_COMP 0xfd /* compressed packet */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
+#define PPP_CCP 0x80fd /* Compression Control Protocol */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQR 0xc025 /* Link Quality Report protocol */
+#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP 0xc029 /* Callback Control Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS 0xffff /* Initial FCS value */
+#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u_char ext_accm[32];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+ NPMODE_PASS, /* pass the packet through */
+ NPMODE_DROP, /* silently drop the packet */
+ NPMODE_ERROR, /* return an error */
+ NPMODE_QUEUE /* save it up for later. */
+};
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp); (cp)++; (s) <<= 8; \
+ (s) |= *(cp); (cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s & 0xff); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp); (cp)++; (l) <<= 8; \
+ (l) |= *(cp); (cp)++; (l) <<= 8; \
+ (l) |= *(cp); (cp)++; (l) <<= 8; \
+ (l) |= *(cp); (cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l))
+#define BCOPY(s, d, l) MEMCPY((d), (s), (l))
+#define BZERO(s, n) memset(s, 0, n)
+
+#if PPP_DEBUG
+#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); }
+#else /* PPP_DEBUG */
+#define PRINTMSG(m, l)
+#endif /* PPP_DEBUG */
+
+/*
+ * MAKEHEADER - Add PPP Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+/*************************
+*** PUBLIC DEFINITIONS ***
+*************************/
+
+/* Error codes. */
+#define PPPERR_NONE 0 /* No error. */
+#define PPPERR_PARAM -1 /* Invalid parameter. */
+#define PPPERR_OPEN -2 /* Unable to open PPP session. */
+#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */
+#define PPPERR_ALLOC -4 /* Unable to allocate resources. */
+#define PPPERR_USER -5 /* User interrupt. */
+#define PPPERR_CONNECT -6 /* Connection lost. */
+#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */
+#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */
+
+/*
+ * PPP IOCTL commands.
+ */
+/*
+ * Get the up status - 0 for down, non-zero for up. The argument must
+ * point to an int.
+ */
+#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */
+#define PPPCTLS_ERRCODE 101 /* Set the error code */
+#define PPPCTLG_ERRCODE 102 /* Get the error code */
+#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */
+
+/************************
+*** PUBLIC DATA TYPES ***
+************************/
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init) (int unit);
+ /* Process a received packet */
+ void (*input) (int unit, u_char *pkt, int len);
+ /* Process a received protocol-reject */
+ void (*protrej) (int unit);
+ /* Lower layer has come up */
+ void (*lowerup) (int unit);
+ /* Lower layer has gone down */
+ void (*lowerdown) (int unit);
+ /* Open the protocol */
+ void (*open) (int unit);
+ /* Close the protocol */
+ void (*close) (int unit, char *reason);
+#if PPP_ADDITIONAL_CALLBACKS
+ /* Print a packet in readable form */
+ int (*printpkt) (u_char *pkt, int len,
+ void (*printer) (void *, char *, ...),
+ void *arg);
+ /* Process a received data packet */
+ void (*datainput) (int unit, u_char *pkt, int len);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ int enabled_flag; /* 0 if protocol is disabled */
+ char *name; /* Text name of protocol */
+#if PPP_ADDITIONAL_CALLBACKS
+ /* Check requested options, assign defaults */
+ void (*check_options) (u_long);
+ /* Configure interface for demand-dial */
+ int (*demand_conf) (int unit);
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) (u_char *pkt, int len);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+ u_short xmit_idle; /* seconds since last NP packet sent */
+ u_short recv_idle; /* seconds since last NP packet received */
+};
+
+struct ppp_settings {
+
+ u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */
+ u_int auth_required : 1; /* Peer is required to authenticate */
+ u_int explicit_remote : 1; /* remote_name specified with remotename opt */
+ u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */
+ u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */
+ u_int usehostname : 1; /* Use hostname for our_name */
+ u_int usepeerdns : 1; /* Ask peer for DNS adds */
+
+ u_short idle_time_limit; /* Shut down link if idle for this long */
+ int maxconnect; /* Maximum connect time (seconds) */
+
+ char user [MAXNAMELEN + 1]; /* Username for PAP */
+ char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */
+ char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */
+ char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */
+};
+
+struct ppp_addrs {
+ ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2;
+};
+
+/*****************************
+*** PUBLIC DATA STRUCTURES ***
+*****************************/
+
+/* Buffers for outgoing packets. */
+extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
+
+extern struct ppp_settings ppp_settings;
+
+extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */
+
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/* Initialize the PPP subsystem. */
+void pppInit(void);
+
+/* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
+ * RFC 1994 says:
+ *
+ * In practice, within or associated with each PPP server, there is a
+ * database which associates "user" names with authentication
+ * information ("secrets"). It is not anticipated that a particular
+ * named user would be authenticated by multiple methods. This would
+ * make the user vulnerable to attacks which negotiate the least secure
+ * method from among a set (such as PAP rather than CHAP). If the same
+ * secret was used, PAP would reveal the secret to be used later with
+ * CHAP.
+ *
+ * Instead, for each user name there should be an indication of exactly
+ * one method used to authenticate that user name. If a user needs to
+ * make use of different authentication methods under different
+ * circumstances, then distinct user names SHOULD be employed, each of
+ * which identifies exactly one authentication method.
+ *
+ */
+enum pppAuthType {
+ PPPAUTHTYPE_NONE,
+ PPPAUTHTYPE_ANY,
+ PPPAUTHTYPE_PAP,
+ PPPAUTHTYPE_CHAP
+};
+
+void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd);
+
+/*
+ * Open a new PPP connection using the given serial I/O device.
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session.
+ * Return a new PPP connection descriptor on success or
+ * an error code (negative) on failure.
+ */
+int pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx);
+
+/*
+ * Open a new PPP Over Ethernet (PPPOE) connection.
+ */
+int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx);
+
+/* for source code compatibility */
+#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls)
+
+/*
+ * Close a PPP connection and release the descriptor.
+ * Any outstanding packets in the queues are dropped.
+ * Return 0 on success, an error code on failure.
+ */
+int pppClose(int pd);
+
+/*
+ * Indicate to the PPP process that the line has disconnected.
+ */
+void pppSigHUP(int pd);
+
+/*
+ * Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure.
+ */
+int pppIOCtl(int pd, int cmd, void *arg);
+
+/*
+ * Return the Maximum Transmission Unit for the given PPP connection.
+ */
+u_short pppMTU(int pd);
+
+/*
+ * Write n characters to a ppp link.
+ * RETURN: >= 0 Number of characters written, -1 Failed to write to device.
+ */
+int pppWrite(int pd, const u_char *s, int n);
+
+void pppInProcOverEthernet(int pd, struct pbuf *pb);
+
+struct pbuf *pppSingleBuf(struct pbuf *p);
+
+void pppLinkTerminated(int pd);
+
+void pppLinkDown(int pd);
+
+void pppos_input(int pd, u_char* data, int len);
+
+/* Configure i/f transmit parameters */
+void ppp_send_config (int, u16_t, u32_t, int, int);
+/* Set extended transmit ACCM */
+void ppp_set_xaccm (int, ext_accm *);
+/* Configure i/f receive parameters */
+void ppp_recv_config (int, int, u32_t, int, int);
+/* Find out how long link has been idle */
+int get_idle_time (int, struct ppp_idle *);
+
+/* Configure VJ TCP header compression */
+int sifvjcomp (int, int, u8_t, u8_t);
+/* Configure i/f down (for IP) */
+int sifup (int);
+/* Set mode for handling packets for proto */
+int sifnpmode (int u, int proto, enum NPmode mode);
+/* Configure i/f down (for IP) */
+int sifdown (int);
+/* Configure IP addresses for i/f */
+int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t);
+/* Reset i/f IP addresses */
+int cifaddr (int, u32_t, u32_t);
+/* Create default route through i/f */
+int sifdefaultroute (int, u32_t, u32_t);
+/* Delete default route through i/f */
+int cifdefaultroute (int, u32_t, u32_t);
+
+/* Get appropriate netmask for address */
+u32_t GetMask (u32_t);
+
+#if LWIP_NETIF_STATUS_CALLBACK
+void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback);
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback);
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#endif /* PPP_SUPPORT */
+
+#endif /* PPP_H */
diff --git a/core/lwip/src/netif/ppp/ppp_oe.c b/core/lwip/src/netif/ppp/ppp_oe.c
new file mode 100644
index 00000000..040a0bc9
--- /dev/null
+++ b/core/lwip/src/netif/ppp/ppp_oe.c
@@ -0,0 +1,1132 @@
+/*****************************************************************************
+* ppp_oe.c - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp_oe.h"
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "lwip/timers.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+#include <stdio.h>
+
+
+/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
+#define PPPOE_ADD_16(PTR, VAL) \
+ *(PTR)++ = (u8_t)((VAL) / 256); \
+ *(PTR)++ = (u8_t)((VAL) % 256)
+
+/* Add a complete PPPoE header to the buffer pointed to by PTR */
+#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \
+ *(PTR)++ = PPPOE_VERTYPE; \
+ *(PTR)++ = (CODE); \
+ PPPOE_ADD_16(PTR, SESS); \
+ PPPOE_ADD_16(PTR, LEN)
+
+#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */
+#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */
+#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */
+#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */
+
+#ifdef PPPOE_SERVER
+#error "PPPOE_SERVER is not yet supported under lwIP!"
+/* from if_spppsubr.c */
+#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */
+#endif
+
+#ifndef PPPOE_ERRORSTRING_LEN
+#define PPPOE_ERRORSTRING_LEN 64
+#endif
+static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN];
+
+
+/* input routines */
+static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *);
+
+/* management routines */
+static int pppoe_do_disconnect(struct pppoe_softc *);
+static void pppoe_abort_connect(struct pppoe_softc *);
+static void pppoe_clear_softc(struct pppoe_softc *, const char *);
+
+/* internal timeout handling */
+static void pppoe_timeout(void *);
+
+/* sending actual protocol controll packets */
+static err_t pppoe_send_padi(struct pppoe_softc *);
+static err_t pppoe_send_padr(struct pppoe_softc *);
+#ifdef PPPOE_SERVER
+static err_t pppoe_send_pado(struct pppoe_softc *);
+static err_t pppoe_send_pads(struct pppoe_softc *);
+#endif
+static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *);
+
+/* internal helper functions */
+static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *);
+static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *);
+
+/** linked list of created pppoe interfaces */
+static struct pppoe_softc *pppoe_softc_list;
+
+err_t
+pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr)
+{
+ struct pppoe_softc *sc;
+
+ sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF);
+ if (sc == NULL) {
+ *scptr = NULL;
+ return ERR_MEM;
+ }
+ memset(sc, 0, sizeof(struct pppoe_softc));
+
+ /* changed to real address later */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+
+ sc->sc_pd = pd;
+ sc->sc_linkStatusCB = linkStatusCB;
+ sc->sc_ethif = ethif;
+
+ /* put the new interface at the head of the list */
+ sc->next = pppoe_softc_list;
+ pppoe_softc_list = sc;
+
+ *scptr = sc;
+
+ return ERR_OK;
+}
+
+err_t
+pppoe_destroy(struct netif *ifp)
+{
+ struct pppoe_softc *sc, *prev = NULL;
+
+ for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) {
+ if (sc->sc_ethif == ifp) {
+ break;
+ }
+ }
+
+ if(!(sc && (sc->sc_ethif == ifp))) {
+ return ERR_IF;
+ }
+
+ sys_untimeout(pppoe_timeout, sc);
+ if (prev == NULL) {
+ /* remove sc from the head of the list */
+ pppoe_softc_list = sc->next;
+ } else {
+ /* remove sc from the list */
+ prev->next = sc->next;
+ }
+
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name) {
+ mem_free(sc->sc_concentrator_name);
+ }
+ if (sc->sc_service_name) {
+ mem_free(sc->sc_service_name);
+ }
+#endif /* PPPOE_TODO */
+ memp_free(MEMP_PPPOE_IF, sc);
+
+ return ERR_OK;
+}
+
+/*
+ * Find the interface handling the specified session.
+ * Note: O(number of sessions open), this is a client-side only, mean
+ * and lean implementation, so number of open sessions typically should
+ * be 1.
+ */
+static struct pppoe_softc *
+pppoe_find_softc_by_session(u_int session, struct netif *rcvif)
+{
+ struct pppoe_softc *sc;
+
+ if (session == 0) {
+ return NULL;
+ }
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc->sc_state == PPPOE_STATE_SESSION
+ && sc->sc_session == session) {
+ if (sc->sc_ethif == rcvif) {
+ return sc;
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Check host unique token passed and return appropriate softc pointer,
+ * or NULL if token is bogus. */
+static struct pppoe_softc *
+pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif)
+{
+ struct pppoe_softc *sc, *t;
+
+ if (pppoe_softc_list == NULL) {
+ return NULL;
+ }
+
+ if (len != sizeof sc) {
+ return NULL;
+ }
+ MEMCPY(&t, token, len);
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc == t) {
+ break;
+ }
+ }
+
+ if (sc == NULL) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n"));
+ return NULL;
+ }
+
+ /* should be safe to access *sc now */
+ if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
+ printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state);
+ return NULL;
+ }
+ if (sc->sc_ethif != rcvif) {
+ printf("%c%c%"U16_F": wrong interface, not accepting host unique\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ return NULL;
+ }
+ return sc;
+}
+
+static void
+pppoe_linkstatus_up(struct pppoe_softc *sc)
+{
+ sc->sc_linkStatusCB(sc->sc_pd, 1);
+}
+
+/* analyze and handle a single received packet while not in session state */
+static void
+pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
+{
+ u16_t tag, len;
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+ const char *err_msg;
+ char devname[6];
+ u8_t *ac_cookie;
+ u16_t ac_cookie_len;
+#ifdef PPPOE_SERVER
+ u8_t *hunique;
+ size_t hunique_len;
+#endif
+ struct pppoehdr *ph;
+ struct pppoetag pt;
+ int off, err, errortag;
+ struct eth_hdr *ethhdr;
+
+ pb = pppSingleBuf(pb);
+
+ strcpy(devname, "pppoe"); /* as long as we don't know which instance */
+ err_msg = NULL;
+ errortag = 0;
+ if (pb->len < sizeof(*ethhdr)) {
+ goto done;
+ }
+ ethhdr = (struct eth_hdr *)pb->payload;
+ off = sizeof(*ethhdr);
+
+ ac_cookie = NULL;
+ ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ hunique = NULL;
+ hunique_len = 0;
+#endif
+ session = 0;
+ if (pb->len - off < PPPOE_HEADERLEN) {
+ printf("pppoe: packet too short: %d\n", pb->len);
+ goto done;
+ }
+
+ ph = (struct pppoehdr *) (ethhdr + 1);
+ if (ph->vertype != PPPOE_VERTYPE) {
+ printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype);
+ goto done;
+ }
+ session = ntohs(ph->session);
+ plen = ntohs(ph->plen);
+ off += sizeof(*ph);
+
+ if (plen + off > pb->len) {
+ printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
+ pb->len - off, plen);
+ goto done;
+ }
+ if(pb->tot_len == pb->len) {
+ pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */
+ }
+ tag = 0;
+ len = 0;
+ sc = NULL;
+ while (off + sizeof(pt) <= pb->len) {
+ MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt));
+ tag = ntohs(pt.tag);
+ len = ntohs(pt.len);
+ if (off + sizeof(pt) + len > pb->len) {
+ printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len);
+ goto done;
+ }
+ switch (tag) {
+ case PPPOE_TAG_EOL:
+ goto breakbreak;
+ case PPPOE_TAG_SNAME:
+ break; /* ignored */
+ case PPPOE_TAG_ACNAME:
+ break; /* ignored */
+ case PPPOE_TAG_HUNIQUE:
+ if (sc != NULL) {
+ break;
+ }
+#ifdef PPPOE_SERVER
+ hunique = (u8_t*)pb->payload + off + sizeof(pt);
+ hunique_len = len;
+#endif
+ sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif);
+ if (sc != NULL) {
+ snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ }
+ break;
+ case PPPOE_TAG_ACCOOKIE:
+ if (ac_cookie == NULL) {
+ ac_cookie = (u8_t*)pb->payload + off + sizeof(pt);
+ ac_cookie_len = len;
+ }
+ break;
+ case PPPOE_TAG_SNAME_ERR:
+ err_msg = "SERVICE NAME ERROR";
+ errortag = 1;
+ break;
+ case PPPOE_TAG_ACSYS_ERR:
+ err_msg = "AC SYSTEM ERROR";
+ errortag = 1;
+ break;
+ case PPPOE_TAG_GENERIC_ERR:
+ err_msg = "GENERIC ERROR";
+ errortag = 1;
+ break;
+ }
+ if (err_msg) {
+ if (errortag && len) {
+ u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1);
+ strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
+ pppoe_error_tmp[error_len-1] = '\0';
+ printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp);
+ } else {
+ printf("%s: %s\n", devname, err_msg);
+ }
+ if (errortag) {
+ goto done;
+ }
+ }
+ off += sizeof(pt) + len;
+ }
+
+breakbreak:;
+ switch (ph->code) {
+ case PPPOE_CODE_PADI:
+#ifdef PPPOE_SERVER
+ /*
+ * got service name, concentrator name, and/or host unique.
+ * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP.
+ */
+ if (LIST_EMPTY(&pppoe_softc_list)) {
+ goto done;
+ }
+ LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+ if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) {
+ continue;
+ }
+ if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ continue;
+ }
+ if (sc->sc_state == PPPOE_STATE_INITIAL) {
+ break;
+ }
+ }
+ if (sc == NULL) {
+ /* printf("pppoe: free passive interface is not found\n"); */
+ goto done;
+ }
+ if (hunique) {
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ }
+ sc->sc_hunique = mem_malloc(hunique_len);
+ if (sc->sc_hunique == NULL) {
+ goto done;
+ }
+ sc->sc_hunique_len = hunique_len;
+ MEMCPY(sc->sc_hunique, hunique, hunique_len);
+ }
+ MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest);
+ sc->sc_state = PPPOE_STATE_PADO_SENT;
+ pppoe_send_pado(sc);
+ break;
+#endif /* PPPOE_SERVER */
+ case PPPOE_CODE_PADR:
+#ifdef PPPOE_SERVER
+ /*
+ * get sc from ac_cookie if IFF_PASSIVE
+ */
+ if (ac_cookie == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ printf("pppoe: received PADR but not includes ac_cookie\n");
+ goto done;
+ }
+ sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif);
+ if (sc == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ if (!LIST_EMPTY(&pppoe_softc_list)) {
+ printf("pppoe: received PADR but could not find request for it\n");
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ goto done;
+ }
+ if (hunique) {
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ }
+ sc->sc_hunique = mem_malloc(hunique_len);
+ if (sc->sc_hunique == NULL) {
+ goto done;
+ }
+ sc->sc_hunique_len = hunique_len;
+ MEMCPY(sc->sc_hunique, hunique, hunique_len);
+ }
+ pppoe_send_pads(sc);
+ sc->sc_state = PPPOE_STATE_SESSION;
+ pppoe_linkstatus_up(sc); /* notify upper layers */
+ break;
+#else
+ /* ignore, we are no access concentrator */
+ goto done;
+#endif /* PPPOE_SERVER */
+ case PPPOE_CODE_PADO:
+ if (sc == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ if (pppoe_softc_list != NULL) {
+ printf("pppoe: received PADO but could not find request for it\n");
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
+ printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ goto done;
+ }
+ if (ac_cookie) {
+ sc->sc_ac_cookie_len = ac_cookie_len;
+ MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len);
+ }
+ MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr));
+ sys_untimeout(pppoe_timeout, sc);
+ sc->sc_padr_retried = 0;
+ sc->sc_state = PPPOE_STATE_PADR_SENT;
+ if ((err = pppoe_send_padr(sc)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+ break;
+ case PPPOE_CODE_PADS:
+ if (sc == NULL) {
+ goto done;
+ }
+ sc->sc_session = session;
+ sys_untimeout(pppoe_timeout, sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session));
+ sc->sc_state = PPPOE_STATE_SESSION;
+ pppoe_linkstatus_up(sc); /* notify upper layers */
+ break;
+ case PPPOE_CODE_PADT:
+ if (sc == NULL) {
+ goto done;
+ }
+ pppoe_clear_softc(sc, "received PADT");
+ break;
+ default:
+ if(sc) {
+ printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+ (u16_t)ph->code, session);
+ } else {
+ printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session);
+ }
+ break;
+ }
+
+done:
+ pbuf_free(pb);
+ return;
+}
+
+void
+pppoe_disc_input(struct netif *netif, struct pbuf *p)
+{
+ /* avoid error messages if there is not a single pppoe instance */
+ if (pppoe_softc_list != NULL) {
+ pppoe_dispatch_disc_pkt(netif, p);
+ } else {
+ pbuf_free(p);
+ }
+}
+
+void
+pppoe_data_input(struct netif *netif, struct pbuf *pb)
+{
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+ struct pppoehdr *ph;
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ u8_t shost[ETHER_ADDR_LEN];
+#endif
+
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost));
+#endif
+ if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ pb = pppSingleBuf (pb);
+
+ if (pb->len <= PPPOE_HEADERLEN) {
+ printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len);
+ goto drop;
+ }
+
+ if (pb->len < sizeof(*ph)) {
+ printf("pppoe_data_input: could not get PPPoE header\n");
+ goto drop;
+ }
+ ph = (struct pppoehdr *)pb->payload;
+
+ if (ph->vertype != PPPOE_VERTYPE) {
+ printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype);
+ goto drop;
+ }
+ if (ph->code != 0) {
+ goto drop;
+ }
+
+ session = ntohs(ph->session);
+ sc = pppoe_find_softc_by_session(session, netif);
+ if (sc == NULL) {
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ printf("pppoe: input for unknown session 0x%x, sending PADT\n", session);
+ pppoe_send_padt(netif, session, shost);
+#endif
+ goto drop;
+ }
+
+ plen = ntohs(ph->plen);
+
+ if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+ pb->len, plen));
+
+ if (pb->len < plen) {
+ goto drop;
+ }
+
+ pppInProcOverEthernet(sc->sc_pd, pb);
+
+ return;
+
+drop:
+ pbuf_free(pb);
+}
+
+static err_t
+pppoe_output(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ struct eth_hdr *ethhdr;
+ u16_t etype;
+ err_t res;
+
+ if (!sc->sc_ethif) {
+ pbuf_free(pb);
+ return ERR_IF;
+ }
+
+ ethhdr = (struct eth_hdr *)pb->payload;
+ etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
+ ethhdr->type = htons(etype);
+ MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+ MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr));
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype,
+ sc->sc_state, sc->sc_session,
+ sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5],
+ pb->tot_len));
+
+ res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb);
+
+ pbuf_free(pb);
+
+ return res;
+}
+
+static err_t
+pppoe_send_padi(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ int len;
+#ifdef PPPOE_TODO
+ int l1 = 0, l2 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ if (sc->sc_state >PPPOE_STATE_PADI_SENT) {
+ PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state));
+ }
+
+ /* calculate length of frame (excluding ethernet header + pppoe header) */
+ len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ l1 = (int)strlen(sc->sc_service_name);
+ len += l1;
+ }
+ if (sc->sc_concentrator_name != NULL) {
+ l2 = (int)strlen(sc->sc_concentrator_name);
+ len += 2 + 2 + l2;
+ }
+#endif /* PPPOE_TODO */
+ LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+ sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ /* fill in pkt */
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name != NULL) {
+ PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
+ PPPOE_ADD_16(p, l2);
+ MEMCPY(p, sc->sc_concentrator_name, l2);
+ p += l2;
+ }
+#endif /* PPPOE_TODO */
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof sc);
+
+ /* send pkt */
+ return pppoe_output(sc, pb);
+}
+
+static void
+pppoe_timeout(void *arg)
+{
+ int retry_wait, err;
+ struct pppoe_softc *sc = (struct pppoe_softc*)arg;
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+
+ switch (sc->sc_state) {
+ case PPPOE_STATE_PADI_SENT:
+ /*
+ * We have two basic ways of retrying:
+ * - Quick retry mode: try a few times in short sequence
+ * - Slow retry mode: we already had a connection successfully
+ * established and will try infinitely (without user
+ * intervention)
+ * We only enter slow retry mode if IFF_LINK1 (aka autodial)
+ * is not set.
+ */
+
+ /* initialize for quick retry mode */
+ retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried);
+
+ sc->sc_padi_retried++;
+ if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
+#if 0
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) {
+ /* slow retry mode */
+ retry_wait = PPPOE_SLOW_RETRY;
+ } else
+#endif
+ {
+ pppoe_abort_connect(sc);
+ return;
+ }
+ }
+ if ((err = pppoe_send_padi(sc)) != 0) {
+ sc->sc_padi_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(retry_wait, pppoe_timeout, sc);
+ break;
+
+ case PPPOE_STATE_PADR_SENT:
+ sc->sc_padr_retried++;
+ if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) {
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ sc->sc_padr_retried = 0;
+ if ((err = pppoe_send_padi(sc)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc);
+ return;
+ }
+ if ((err = pppoe_send_padr(sc)) != 0) {
+ sc->sc_padr_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+ break;
+ case PPPOE_STATE_CLOSING:
+ pppoe_do_disconnect(sc);
+ break;
+ default:
+ return; /* all done, work in peace */
+ }
+}
+
+/* Start a connection (i.e. initiate discovery phase) */
+int
+pppoe_connect(struct pppoe_softc *sc)
+{
+ int err;
+
+ if (sc->sc_state != PPPOE_STATE_INITIAL) {
+ return EBUSY;
+ }
+
+#ifdef PPPOE_SERVER
+ /* wait PADI if IFF_PASSIVE */
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ return 0;
+ }
+#endif
+ /* save state, in case we fail to send PADI */
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ sc->sc_padr_retried = 0;
+ err = pppoe_send_padi(sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc);
+ return err;
+}
+
+/* disconnect */
+void
+pppoe_disconnect(struct pppoe_softc *sc)
+{
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ return;
+ }
+ /*
+ * Do not call pppoe_disconnect here, the upper layer state
+ * machine gets confused by this. We must return from this
+ * function and defer disconnecting to the timeout handler.
+ */
+ sc->sc_state = PPPOE_STATE_CLOSING;
+ sys_timeout(20, pppoe_timeout, sc);
+}
+
+static int
+pppoe_do_disconnect(struct pppoe_softc *sc)
+{
+ int err;
+
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ err = EBUSY;
+ } else {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
+ }
+
+ /* cleanup softc */
+ sc->sc_state = PPPOE_STATE_INITIAL;
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ sc->sc_hunique = NULL;
+ }
+ sc->sc_hunique_len = 0;
+#endif
+ sc->sc_session = 0;
+
+ sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
+
+ return err;
+}
+
+/* Connection attempt aborted */
+static void
+pppoe_abort_connect(struct pppoe_softc *sc)
+{
+ printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ sc->sc_state = PPPOE_STATE_CLOSING;
+
+ sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
+
+ /* clear connection state */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_state = PPPOE_STATE_INITIAL;
+}
+
+/* Send a PADR packet */
+static err_t
+pppoe_send_padr(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len;
+#ifdef PPPOE_TODO
+ size_t l1 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ if (sc->sc_state != PPPOE_STATE_PADR_SENT) {
+ return ERR_CONN;
+ }
+
+ len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+#endif /* PPPOE_TODO */
+ if (sc->sc_ac_cookie_len > 0) {
+ len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */
+ }
+ LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+ sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+ if (sc->sc_ac_cookie_len > 0) {
+ PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+ PPPOE_ADD_16(p, sc->sc_ac_cookie_len);
+ MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len);
+ p += sc->sc_ac_cookie_len;
+ }
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof sc);
+
+ return pppoe_output(sc, pb);
+}
+
+/* send a PADT packet */
+static err_t
+pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest)
+{
+ struct pbuf *pb;
+ struct eth_hdr *ethhdr;
+ err_t res;
+ u8_t *p;
+
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ ethhdr = (struct eth_hdr *)pb->payload;
+ ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC);
+ MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
+ MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr));
+
+ p = (u8_t*)(ethhdr + 1);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0);
+
+ res = outgoing_if->linkoutput(outgoing_if, pb);
+
+ pbuf_free(pb);
+
+ return res;
+}
+
+#ifdef PPPOE_SERVER
+static err_t
+pppoe_send_pado(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len;
+
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ return ERR_CONN;
+ }
+
+ /* calc length */
+ len = 0;
+ /* include ac_cookie */
+ len += 2 + 2 + sizeof(sc);
+ /* include hunique */
+ len += 2 + 2 + sc->sc_hunique_len;
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof(sc));
+ p += sizeof(sc);
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sc->sc_hunique_len);
+ MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+ return pppoe_output(sc, pb);
+}
+
+static err_t
+pppoe_send_pads(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len, l1 = 0; /* XXX: gcc */
+
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ return ERR_CONN;
+ }
+
+ sc->sc_session = mono_time.tv_sec % 0xff + 1;
+ /* calc length */
+ len = 0;
+ /* include hunique */
+ len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else {
+ PPPOE_ADD_16(p, 0);
+ }
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sc->sc_hunique_len);
+ MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+ return pppoe_output(sc, pb);
+}
+#endif
+
+err_t
+pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ u8_t *p;
+ size_t len;
+
+ /* are we ready to process data yet? */
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ /*sppp_flush(&sc->sc_sppp.pp_if);*/
+ pbuf_free(pb);
+ return ERR_CONN;
+ }
+
+ len = pb->tot_len;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ LINK_STATS_INC(link.lenerr);
+ pbuf_free(pb);
+ return ERR_BUF;
+ }
+
+ p = (u8_t*)pb->payload + sizeof(struct eth_hdr);
+ PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
+
+ return pppoe_output(sc, pb);
+}
+
+#if 0 /*def PFIL_HOOKS*/
+static int
+pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
+{
+ struct pppoe_softc *sc;
+ int s;
+
+ if (mp != (struct pbuf **)PFIL_IFNET_DETACH) {
+ return 0;
+ }
+
+ LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+ if (sc->sc_ethif != ifp) {
+ continue;
+ }
+ if (sc->sc_sppp.pp_if.if_flags & IFF_UP) {
+ sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
+ printf("%c%c%"U16_F": ethernet interface detached, going down\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ }
+ sc->sc_ethif = NULL;
+ pppoe_clear_softc(sc, "ethernet interface detached");
+ }
+
+ return 0;
+}
+#endif
+
+static void
+pppoe_clear_softc(struct pppoe_softc *sc, const char *message)
+{
+ LWIP_UNUSED_ARG(message);
+
+ /* stop timer */
+ sys_untimeout(pppoe_timeout, sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message));
+
+ /* fix our state */
+ sc->sc_state = PPPOE_STATE_INITIAL;
+
+ /* notify upper layers */
+ sc->sc_linkStatusCB(sc->sc_pd, 0);
+
+ /* clean up softc */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_ac_cookie_len = 0;
+ sc->sc_session = 0;
+}
+
+#endif /* PPPOE_SUPPORT */
+
diff --git a/core/lwip/src/netif/ppp/pppdebug.h b/core/lwip/src/netif/ppp/pppdebug.h
new file mode 100644
index 00000000..81349971
--- /dev/null
+++ b/core/lwip/src/netif/ppp/pppdebug.h
@@ -0,0 +1,73 @@
+/*****************************************************************************
+* pppdebug.h - System debugging utilities.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+* portions Copyright (c) 2001 by Cognizant Pty Ltd.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY (please don't use tabs!)
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-07-29 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*
+*****************************************************************************
+*/
+#ifndef PPPDEBUG_H
+#define PPPDEBUG_H
+
+/* Trace levels. */
+#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_INFO (PPP_DEBUG)
+#define LOG_DETAIL (PPP_DEBUG)
+#define LOG_DEBUG (PPP_DEBUG)
+
+
+#define TRACELCP PPP_DEBUG
+
+#if PPP_DEBUG
+
+#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b)
+
+#else /* PPP_DEBUG */
+
+#define AUTHDEBUG(a, b)
+#define IPCPDEBUG(a, b)
+#define UPAPDEBUG(a, b)
+#define LCPDEBUG(a, b)
+#define FSMDEBUG(a, b)
+#define CHAPDEBUG(a, b)
+#define PPPDEBUG(a, b)
+
+#endif /* PPP_DEBUG */
+
+#endif /* PPPDEBUG_H */
diff --git a/core/lwip/src/netif/ppp/randm.c b/core/lwip/src/netif/ppp/randm.c
new file mode 100644
index 00000000..2f35caf6
--- /dev/null
+++ b/core/lwip/src/netif/ppp/randm.c
@@ -0,0 +1,249 @@
+/*****************************************************************************
+* randm.c - Random number generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-06-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Extracted from avos.
+*****************************************************************************/
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "md5.h"
+#include "randm.h"
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include <string.h>
+
+#if MD5_SUPPORT /* this module depends on MD5 */
+#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+static char randPool[RANDPOOLSZ]; /* Pool of randomness. */
+static long randCount = 0; /* Pseudo-random incrementer */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/*
+ * Initialize the random number generator.
+ *
+ * Since this is to be called on power up, we don't have much
+ * system randomess to work with. Here all we use is the
+ * real-time clock. We'll accumulate more randomness as soon
+ * as things start happening.
+ */
+void
+avRandomInit()
+{
+ avChurnRand(NULL, 0);
+}
+
+/*
+ * Churn the randomness pool on a random event. Call this early and often
+ * on random and semi-random system events to build randomness in time for
+ * usage. For randomly timed events, pass a null pointer and a zero length
+ * and this will use the system timer and other sources to add randomness.
+ * If new random data is available, pass a pointer to that and it will be
+ * included.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ */
+void
+avChurnRand(char *randData, u32_t randLen)
+{
+ MD5_CTX md5;
+
+ /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */
+ MD5Init(&md5);
+ MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
+ if (randData) {
+ MD5Update(&md5, (u_char *)randData, randLen);
+ } else {
+ struct {
+ /* INCLUDE fields for any system sources of randomness */
+ char foobar;
+ } sysData;
+
+ /* Load sysData fields here. */
+ MD5Update(&md5, (u_char *)&sysData, sizeof(sysData));
+ }
+ MD5Final((u_char *)randPool, &md5);
+/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */
+}
+
+/*
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using churnRand().
+ * Note: It's important that there be sufficient randomness in randPool
+ * before this is called for otherwise the range of the result may be
+ * narrow enough to make a search feasible.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ *
+ * XXX Why does he not just call churnRand() for each block? Probably
+ * so that you don't ever publish the seed which could possibly help
+ * predict future values.
+ * XXX Why don't we preserve md5 between blocks and just update it with
+ * randCount each time? Probably there is a weakness but I wish that
+ * it was documented.
+ */
+void
+avGenRand(char *buf, u32_t bufLen)
+{
+ MD5_CTX md5;
+ u_char tmp[16];
+ u32_t n;
+
+ while (bufLen > 0) {
+ n = LWIP_MIN(bufLen, RANDPOOLSZ);
+ MD5Init(&md5);
+ MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
+ MD5Update(&md5, (u_char *)&randCount, sizeof(randCount));
+ MD5Final(tmp, &md5);
+ randCount++;
+ MEMCPY(buf, tmp, n);
+ buf += n;
+ bufLen -= n;
+ }
+}
+
+/*
+ * Return a new random number.
+ */
+u32_t
+avRandom()
+{
+ u32_t newRand;
+
+ avGenRand((char *)&newRand, sizeof(newRand));
+
+ return newRand;
+}
+
+#else /* MD5_SUPPORT */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+static int avRandomized = 0; /* Set when truely randomized. */
+static u32_t avRandomSeed = 0; /* Seed used for random number generation. */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/*
+ * Initialize the random number generator.
+ *
+ * Here we attempt to compute a random number seed but even if
+ * it isn't random, we'll randomize it later.
+ *
+ * The current method uses the fields from the real time clock,
+ * the idle process counter, the millisecond counter, and the
+ * hardware timer tick counter. When this is invoked
+ * in startup(), then the idle counter and timer values may
+ * repeat after each boot and the real time clock may not be
+ * operational. Thus we call it again on the first random
+ * event.
+ */
+void
+avRandomInit()
+{
+#if 0
+ /* Get a pointer into the last 4 bytes of clockBuf. */
+ u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]);
+
+ /*
+ * Initialize our seed using the real-time clock, the idle
+ * counter, the millisecond timer, and the hardware timer
+ * tick counter. The real-time clock and the hardware
+ * tick counter are the best sources of randomness but
+ * since the tick counter is only 16 bit (and truncated
+ * at that), the idle counter and millisecond timer
+ * (which may be small values) are added to help
+ * randomize the lower 16 bits of the seed.
+ */
+ readClk();
+ avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr
+ + ppp_mtime() + ((u32_t)TM1 << 16) + TM1;
+#else
+ avRandomSeed += sys_jiffies(); /* XXX */
+#endif
+
+ /* Initialize the Borland random number generator. */
+ srand((unsigned)avRandomSeed);
+}
+
+/*
+ * Randomize our random seed value. Here we use the fact that
+ * this function is called at *truely random* times by the polling
+ * and network functions. Here we only get 16 bits of new random
+ * value but we use the previous value to randomize the other 16
+ * bits.
+ */
+void
+avRandomize(void)
+{
+ static u32_t last_jiffies;
+
+ if (!avRandomized) {
+ avRandomized = !0;
+ avRandomInit();
+ /* The initialization function also updates the seed. */
+ } else {
+ /* avRandomSeed += (avRandomSeed << 16) + TM1; */
+ avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */
+ }
+ last_jiffies = sys_jiffies();
+}
+
+/*
+ * Return a new random number.
+ * Here we use the Borland rand() function to supply a pseudo random
+ * number which we make truely random by combining it with our own
+ * seed which is randomized by truely random events.
+ * Thus the numbers will be truely random unless there have been no
+ * operator or network events in which case it will be pseudo random
+ * seeded by the real time clock.
+ */
+u32_t
+avRandom()
+{
+ return ((((u32_t)rand() << 16) + rand()) + avRandomSeed);
+}
+
+#endif /* MD5_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/randm.h b/core/lwip/src/netif/ppp/randm.h
new file mode 100644
index 00000000..a0984b02
--- /dev/null
+++ b/core/lwip/src/netif/ppp/randm.h
@@ -0,0 +1,81 @@
+/*****************************************************************************
+* randm.h - Random number generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-05-29 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Extracted from avos.
+*****************************************************************************/
+
+#ifndef RANDM_H
+#define RANDM_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+/*
+ * Initialize the random number generator.
+ */
+void avRandomInit(void);
+
+/*
+ * Churn the randomness pool on a random event. Call this early and often
+ * on random and semi-random system events to build randomness in time for
+ * usage. For randomly timed events, pass a null pointer and a zero length
+ * and this will use the system timer and other sources to add randomness.
+ * If new random data is available, pass a pointer to that and it will be
+ * included.
+ */
+void avChurnRand(char *randData, u32_t randLen);
+
+/*
+ * Randomize our random seed value. To be called for truely random events
+ * such as user operations and network traffic.
+ */
+#if MD5_SUPPORT
+#define avRandomize() avChurnRand(NULL, 0)
+#else /* MD5_SUPPORT */
+void avRandomize(void);
+#endif /* MD5_SUPPORT */
+
+/*
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using churnRand().
+ * Thus it's important to make sure that the results of this are not
+ * published directly because one could predict the next result to at
+ * least some degree. Also, it's important to get a good seed before
+ * the first use.
+ */
+void avGenRand(char *buf, u32_t bufLen);
+
+/*
+ * Return a new random number.
+ */
+u32_t avRandom(void);
+
+
+#endif /* RANDM_H */
diff --git a/core/lwip/src/netif/ppp/vj.c b/core/lwip/src/netif/ppp/vj.c
new file mode 100644
index 00000000..b7f2d54c
--- /dev/null
+++ b/core/lwip/src/netif/ppp/vj.c
@@ -0,0 +1,652 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * Initial distribution.
+ *
+ * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
+ * so that the entire packet being decompressed doesn't have
+ * to be in contiguous memory (just the compressed header).
+ *
+ * Modified March 1998 by Guy Lancaster, glanca@gesn.com,
+ * for a 16 bit processor.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#include "vj.h"
+
+#include <string.h>
+
+#if VJ_SUPPORT
+
+#if LINK_STATS
+#define INCR(counter) ++comp->stats.counter
+#else
+#define INCR(counter)
+#endif
+
+void
+vj_compress_init(struct vjcompress *comp)
+{
+ register u_char i;
+ register struct cstate *tstate = comp->tstate;
+
+#if MAX_SLOTS == 0
+ memset((char *)comp, 0, sizeof(*comp));
+#endif
+ comp->maxSlotIndex = MAX_SLOTS - 1;
+ comp->compressSlot = 0; /* Disable slot ID compression by default. */
+ for (i = MAX_SLOTS - 1; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = VJF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the long, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (u_char)(n); \
+ cp[0] = (u_char)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u_char)(n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (u_char)(n); \
+ cp[0] = (u_char)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u_char)(n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = htonl(tmp); \
+ cp += 3; \
+ } else { \
+ u32_t tmp = ntohl(f) + (u32_t)*cp++; \
+ (f) = htonl(tmp); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \
+ (f) = htons(tmp); \
+ cp += 3; \
+ } else { \
+ u_short tmp = ntohs(f) + (u_short)*cp++; \
+ (f) = htons(tmp); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons(((u_short)cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_short)*cp++); \
+ } \
+}
+
+/*
+ * vj_compress_tcp - Attempt to do Van Jacobson header compression on a
+ * packet. This assumes that nb and comp are not null and that the first
+ * buffer of the chain contains a valid IP header.
+ * Return the VJ type code indicating whether or not the packet was
+ * compressed.
+ */
+u_int
+vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
+{
+ register struct ip_hdr *ip = (struct ip_hdr *)pb->payload;
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_short hlen = IPH_HL(ip);
+ register struct tcp_hdr *oth;
+ register struct tcp_hdr *th;
+ register u_short deltaS, deltaA;
+ register u_long deltaL;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Check that the packet is IP proto TCP.
+ */
+ if (IPH_PROTO(ip) != IP_PROTO_TCP) {
+ return (TYPE_IP);
+ }
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't
+ * `compressible' (i.e., ACK isn't set or some other control bit is
+ * set).
+ */
+ if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) {
+ return (TYPE_IP);
+ }
+ th = (struct tcp_hdr *)&((long *)ip)[hlen];
+ if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
+ return (TYPE_IP);
+ }
+ /*
+ * Packet is compressible -- we're going to send either a
+ * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
+ * to locate (or create) the connection state. Special case the
+ * most recently used connection since it's most likely to be used
+ * again & we don't have to do any reordering if it's used.
+ */
+ INCR(vjs_packets);
+ if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src)
+ || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with
+ * last_cs pointing to the end of the list. The
+ * list is kept in lru order by moving a state to the
+ * head of the list whenever it is referenced. Since
+ * the list is short and, empirically, the connection
+ * we want is almost always near the front, we locate
+ * states via linear search. If we don't find a state
+ * for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs; cs = cs->cs_next;
+ INCR(vjs_searches);
+ if (ip_addr_cmp(&ip->src, &cs->cs_ip.src)
+ && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ goto found;
+ }
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * last_cs to update the lru linkage.
+ */
+ INCR(vjs_misses);
+ comp->last_cs = lcs;
+ hlen += TCPH_OFFSET(th);
+ hlen <<= 2;
+ /* Check that the IP/TCP headers are contained in the first buffer. */
+ if (hlen > pb->len) {
+ return (TYPE_IP);
+ }
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs) {
+ comp->last_cs = lcs;
+ } else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += TCPH_OFFSET(th);
+ hlen <<= 2;
+ /* Check that the IP/TCP headers are contained in the first buffer. */
+ if (hlen > pb->len) {
+ PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
+ return (TYPE_IP);
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first
+ * line of the `if' checks the IP protocol version, header length &
+ * type of service. The 2nd line checks the "Don't fragment" bit.
+ * The 3rd line checks the time-to-live and protocol (the protocol
+ * check is unnecessary but costless). The 4th line checks the TCP
+ * header length. The 5th line checks IP options, if any. The 6th
+ * line checks TCP options, if any. If any of these things are
+ * different between the previous & current datagram, we send the
+ * current datagram `uncompressed'.
+ */
+ if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0]
+ || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3]
+ || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4]
+ || TCPH_OFFSET(th) != TCPH_OFFSET(oth)
+ || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
+ || (TCPH_OFFSET(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_OFFSET(th) - 5) << 2))) {
+ goto uncompressed;
+ }
+
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (TCPH_FLAGS(th) & TCP_URG) {
+ deltaS = ntohs(th->urgp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->urgp != oth->urgp) {
+ /* argh! URG not set but urp changed -- a sensible
+ * implementation should never do this but RFC793
+ * doesn't prohibit the change so we have to deal
+ * with it. */
+ goto uncompressed;
+ }
+
+ if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+
+ if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) {
+ if (deltaL > 0xffff) {
+ goto uncompressed;
+ }
+ deltaA = (u_short)deltaL;
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+
+ if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) {
+ if (deltaL > 0xffff) {
+ goto uncompressed;
+ }
+ deltaS = (u_short)deltaL;
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+
+ switch(changes) {
+ case 0:
+ /*
+ * Nothing changed. If this packet contains data and the
+ * last one didn't, this is probably a data packet following
+ * an ack (normal on an interactive connection) and we send
+ * it compressed. Otherwise it's probably a retransmit,
+ * retransmitted ack or window probe. Send it uncompressed
+ * in case the other side missed the compressed version.
+ */
+ if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) &&
+ ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
+ break;
+ }
+
+ /* (fall through) */
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /*
+ * actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+
+ case NEW_S|NEW_A:
+ if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+
+ case NEW_S:
+ if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ deltaS = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip)));
+ if (deltaS != 1) {
+ ENCODEZ(deltaS);
+ changes |= NEW_I;
+ }
+ if (TCPH_FLAGS(th) & TCP_PSH) {
+ changes |= TCP_PUSH_BIT;
+ }
+ /*
+ * Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->chksum);
+ BCOPY(ip, &cs->cs_ip, hlen);
+
+ /*
+ * We want to use the original packet as our compressed packet.
+ * (cp - new_seq) is the number of bytes we need for compressed
+ * sequence numbers. In addition we need one byte for the change
+ * mask, one for the connection id and two for the tcp checksum.
+ * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
+ * many bytes of the original packet to toss so subtract the two to
+ * get the new packet size.
+ */
+ deltaS = (u_short)(cp - new_seq);
+ if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ if(pbuf_header(pb, -hlen)){
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ }
+ cp = (u_char *)pb->payload;
+ *cp++ = (u_char)(changes | NEW_C);
+ *cp++ = cs->cs_id;
+ } else {
+ hlen -= deltaS + 3;
+ if(pbuf_header(pb, -hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ }
+ cp = (u_char *)pb->payload;
+ *cp++ = (u_char)changes;
+ }
+ *cp++ = (u_char)(deltaA >> 8);
+ *cp++ = (u_char)deltaA;
+ BCOPY(new_seq, cp, deltaS);
+ INCR(vjs_compressed);
+ return (TYPE_COMPRESSED_TCP);
+
+ /*
+ * Update connection state cs & send uncompressed packet (that is,
+ * a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+uncompressed:
+ BCOPY(ip, &cs->cs_ip, hlen);
+ IPH_PROTO_SET(ip, cs->cs_id);
+ comp->last_xmit = cs->cs_id;
+ return (TYPE_UNCOMPRESSED_TCP);
+}
+
+/*
+ * Called when we may have missed a packet.
+ */
+void
+vj_uncompress_err(struct vjcompress *comp)
+{
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+}
+
+/*
+ * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
+ * Return 0 on success, -1 on failure.
+ */
+int
+vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
+{
+ register u_int hlen;
+ register struct cstate *cs;
+ register struct ip_hdr *ip;
+
+ ip = (struct ip_hdr *)nb->payload;
+ hlen = IPH_HL(ip) << 2;
+ if (IPH_PROTO(ip) >= MAX_SLOTS
+ || hlen + sizeof(struct tcp_hdr) > nb->len
+ || (hlen += TCPH_OFFSET(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2)
+ > nb->len
+ || hlen > MAX_HDR) {
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
+ IPH_PROTO(ip), hlen, nb->len));
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return -1;
+ }
+ cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
+ comp->flags &=~ VJF_TOSS;
+ IPH_PROTO_SET(ip, IP_PROTO_TCP);
+ BCOPY(ip, &cs->cs_ip, hlen);
+ cs->cs_hlen = (u_short)hlen;
+ INCR(vjs_uncompressedin);
+ return 0;
+}
+
+/*
+ * Uncompress a packet of type TYPE_COMPRESSED_TCP.
+ * The packet is composed of a buffer chain and the first buffer
+ * must contain an accurate chain length.
+ * The first buffer must include the entire compressed TCP/IP header.
+ * This procedure replaces the compressed header with the uncompressed
+ * header and returns the length of the VJ header.
+ */
+int
+vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
+{
+ u_char *cp;
+ struct tcp_hdr *th;
+ struct cstate *cs;
+ u_short *bp;
+ struct pbuf *n0 = *nb;
+ u32_t tmp;
+ u_int vjlen, hlen, changes;
+
+ INCR(vjs_compressedin);
+ cp = (u_char *)n0->payload;
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /*
+ * Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag.
+ */
+ if (*cp >= MAX_SLOTS) {
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp));
+ goto bad;
+ }
+
+ comp->flags &=~ VJF_TOSS;
+ comp->last_recv = *cp++;
+ } else {
+ /*
+ * this packet has an implicit state index. If we've
+ * had a line error since the last time we got an
+ * explicit state index, we have to toss the packet.
+ */
+ if (comp->flags & VJF_TOSS) {
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n"));
+ INCR(vjs_tossed);
+ return (-1);
+ }
+ }
+ cs = &comp->rstate[comp->last_recv];
+ hlen = IPH_HL(&cs->cs_ip) << 2;
+ th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen];
+ th->chksum = htons((*cp << 8) | cp[1]);
+ cp += 2;
+ if (changes & TCP_PUSH_BIT) {
+ TCPH_SET_FLAG(th, TCP_PSH);
+ } else {
+ TCPH_UNSET_FLAG(th, TCP_PSH);
+ }
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I:
+ {
+ register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ /* some compilers can't nest inline assembler.. */
+ tmp = ntohl(th->ackno) + i;
+ th->ackno = htonl(tmp);
+ tmp = ntohl(th->seqno) + i;
+ th->seqno = htonl(tmp);
+ }
+ break;
+
+ case SPECIAL_D:
+ /* some compilers can't nest inline assembler.. */
+ tmp = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ th->seqno = htonl(tmp);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ TCPH_SET_FLAG(th, TCP_URG);
+ DECODEU(th->urgp);
+ } else {
+ TCPH_UNSET_FLAG(th, TCP_URG);
+ }
+ if (changes & NEW_W) {
+ DECODES(th->wnd);
+ }
+ if (changes & NEW_A) {
+ DECODEL(th->ackno);
+ }
+ if (changes & NEW_S) {
+ DECODEL(th->seqno);
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ DECODES(cs->cs_ip._id);
+ } else {
+ IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1);
+ IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip)));
+ }
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. Fill in the IP total length and update the IP
+ * header checksum.
+ */
+ vjlen = (u_short)(cp - (u_char*)n0->payload);
+ if (n0->len < vjlen) {
+ /*
+ * We must have dropped some characters (crc should detect
+ * this but the old slip framing won't)
+ */
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
+ n0->len, vjlen));
+ goto bad;
+ }
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ tmp = n0->tot_len - vjlen + cs->cs_hlen;
+ IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp));
+#else
+ IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen));
+#endif
+
+ /* recompute the ip header checksum */
+ bp = (u_short *) &cs->cs_ip;
+ IPH_CHKSUM_SET(&cs->cs_ip, 0);
+ for (tmp = 0; hlen > 0; hlen -= 2) {
+ tmp += *bp++;
+ }
+ tmp = (tmp & 0xffff) + (tmp >> 16);
+ tmp = (tmp & 0xffff) + (tmp >> 16);
+ IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp));
+
+ /* Remove the compressed header and prepend the uncompressed header. */
+ if(pbuf_header(n0, -((s16_t)(vjlen)))) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
+ struct pbuf *np, *q;
+ u8_t *bufptr;
+
+ np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
+ if(!np) {
+ PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n"));
+ goto bad;
+ }
+
+ if(pbuf_header(np, -cs->cs_hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ bufptr = n0->payload;
+ for(q = np; q != NULL; q = q->next) {
+ MEMCPY(q->payload, bufptr, q->len);
+ bufptr += q->len;
+ }
+
+ if(n0->next) {
+ pbuf_chain(np, n0->next);
+ pbuf_dechain(n0);
+ }
+ pbuf_free(n0);
+ n0 = np;
+ }
+
+ if(pbuf_header(n0, cs->cs_hlen)) {
+ struct pbuf *np;
+
+ LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
+ np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL);
+ if(!np) {
+ PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n"));
+ goto bad;
+ }
+ pbuf_cat(np, n0);
+ n0 = np;
+ }
+ LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen);
+ MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen);
+
+ *nb = n0;
+
+ return vjlen;
+
+bad:
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return (-1);
+}
+
+#endif /* VJ_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/core/lwip/src/netif/ppp/vj.h b/core/lwip/src/netif/ppp/vj.h
new file mode 100644
index 00000000..fad12136
--- /dev/null
+++ b/core/lwip/src/netif/ppp/vj.h
@@ -0,0 +1,156 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#ifndef VJ_H
+#define VJ_H
+
+#include "lwip/ip.h"
+#include "lwip/tcp_impl.h"
+
+#define MAX_SLOTS 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used state (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */
+ } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+
+struct vjstat {
+ unsigned long vjs_packets; /* outbound packets */
+ unsigned long vjs_compressed; /* outbound compressed packets */
+ unsigned long vjs_searches; /* searches for connection state */
+ unsigned long vjs_misses; /* times couldn't find conn. state */
+ unsigned long vjs_uncompressedin; /* inbound uncompressed packets */
+ unsigned long vjs_compressedin; /* inbound compressed packets */
+ unsigned long vjs_errorin; /* inbound unknown type packets */
+ unsigned long vjs_tossed; /* inbound packets tossed because of error */
+};
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+ u_char maxSlotIndex;
+ u_char compressSlot; /* Flag indicating OK to compress slot ID. */
+#if LINK_STATS
+ struct vjstat stats;
+#endif
+ struct cstate tstate[MAX_SLOTS]; /* xmit connection states */
+ struct cstate rstate[MAX_SLOTS]; /* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1U /* tossing rcvd frames because of input err */
+
+extern void vj_compress_init (struct vjcompress *comp);
+extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb);
+extern void vj_uncompress_err (struct vjcompress *comp);
+extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp);
+extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp);
+
+#endif /* VJ_H */
diff --git a/core/lwip/src/netif/slipif.c b/core/lwip/src/netif/slipif.c
new file mode 100644
index 00000000..c19333dd
--- /dev/null
+++ b/core/lwip/src/netif/slipif.c
@@ -0,0 +1,367 @@
+/**
+ * @file
+ * SLIP Interface
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is built upon the file: src/arch/rtxc/netif/sioslip.c
+ *
+ * Author: Magnus Ivarsson <magnus.ivarsson(at)volvo.com>
+ */
+
+/*
+ * This is an arch independent SLIP netif. The specific serial hooks must be
+ * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send
+ */
+
+#include "netif/slipif.h"
+#include "lwip/opt.h"
+
+#if LWIP_HAVE_SLIPIF
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/sio.h"
+
+#define SLIP_BLOCK 1
+#define SLIP_DONTBLOCK 0
+
+#define SLIP_END 0300 /* 0xC0 */
+#define SLIP_ESC 0333 /* 0xDB */
+#define SLIP_ESC_END 0334 /* 0xDC */
+#define SLIP_ESC_ESC 0335 /* 0xDD */
+
+#define SLIP_MAX_SIZE 1500
+
+enum slipif_recv_state {
+ SLIP_RECV_NORMAL,
+ SLIP_RECV_ESCAPE,
+};
+
+struct slipif_priv {
+ sio_fd_t sd;
+ /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */
+ struct pbuf *p, *q;
+ enum slipif_recv_state state;
+ u16_t i, recved;
+};
+
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chaing packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+err_t
+slipif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ struct slipif_priv *priv;
+ struct pbuf *q;
+ u16_t i;
+ u8_t c;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ LWIP_ASSERT("p != NULL", (p != NULL));
+
+ LWIP_UNUSED_ARG(ipaddr);
+
+ priv = netif->state;
+
+ /* Send pbuf out on the serial I/O device. */
+ sio_send(SLIP_END, priv->sd);
+
+ for (q = p; q != NULL; q = q->next) {
+ for (i = 0; i < q->len; i++) {
+ c = ((u8_t *)q->payload)[i];
+ switch (c) {
+ case SLIP_END:
+ sio_send(SLIP_ESC, priv->sd);
+ sio_send(SLIP_ESC_END, priv->sd);
+ break;
+ case SLIP_ESC:
+ sio_send(SLIP_ESC, priv->sd);
+ sio_send(SLIP_ESC_ESC, priv->sd);
+ break;
+ default:
+ sio_send(c, priv->sd);
+ break;
+ }
+ }
+ }
+ sio_send(SLIP_END, priv->sd);
+ return ERR_OK;
+}
+
+/**
+ * Static function for easy use of blockig or non-blocking
+ * sio_read
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @param block if 1, call sio_read; if 0, call sio_tryread
+ * @return return value of sio_read of sio_tryread
+ */
+static u32_t
+slip_sio_read(sio_fd_t fd, u8_t* data, u32_t len, u8_t block)
+{
+ if (block) {
+ return sio_read(fd, data, len);
+ } else {
+ return sio_tryread(fd, data, len);
+ }
+}
+
+/**
+ * Handle the incoming SLIP stream character by character
+ *
+ * Poll the serial layer by calling sio_read() or sio_tryread().
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param block if 1, block until data is received; if 0, return when all data
+ * from the buffer is received (multiple calls to this function will
+ * return a complete packet, NULL is returned before - used for polling)
+ * @return The IP packet when SLIP_END is received
+ */
+static struct pbuf *
+slipif_input(struct netif *netif, u8_t block)
+{
+ struct slipif_priv *priv;
+ u8_t c;
+ struct pbuf *t;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = netif->state;
+
+ while (slip_sio_read(priv->sd, &c, 1, block) > 0) {
+ switch (priv->state) {
+ case SLIP_RECV_NORMAL:
+ switch (c) {
+ case SLIP_END:
+ if (priv->recved > 0) {
+ /* Received whole packet. */
+ /* Trim the pbuf to the size of the received packet. */
+ pbuf_realloc(priv->q, priv->recved);
+
+ LINK_STATS_INC(link.recv);
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n"));
+ t = priv->q;
+ priv->p = priv->q = NULL;
+ priv->i = priv->recved = 0;
+ return t;
+ }
+ continue;
+ case SLIP_ESC:
+ priv->state = SLIP_RECV_ESCAPE;
+ continue;
+ }
+ break;
+ case SLIP_RECV_ESCAPE:
+ switch (c) {
+ case SLIP_ESC_END:
+ c = SLIP_END;
+ break;
+ case SLIP_ESC_ESC:
+ c = SLIP_ESC;
+ break;
+ }
+ priv->state = SLIP_RECV_NORMAL;
+ /* FALLTHROUGH */
+ }
+
+ /* byte received, packet not yet completely received */
+ if (priv->p == NULL) {
+ /* allocate a new pbuf */
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n"));
+ priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL);
+
+ if (priv->p == NULL) {
+ LINK_STATS_INC(link.drop);
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n"));
+ /* don't process any further since we got no pbuf to receive to */
+ break;
+ }
+
+ if (priv->q != NULL) {
+ /* 'chain' the pbuf to the existing chain */
+ pbuf_cat(priv->q, priv->p);
+ } else {
+ /* p is the first pbuf in the chain */
+ priv->q = priv->p;
+ }
+ }
+
+ /* this automatically drops bytes if > SLIP_MAX_SIZE */
+ if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) {
+ ((u8_t *)priv->p->payload)[priv->i] = c;
+ priv->recved++;
+ priv->i++;
+ if (priv->i >= priv->p->len) {
+ /* on to the next pbuf */
+ priv->i = 0;
+ if (priv->p->next != NULL && priv->p->next->len > 0) {
+ /* p is a chain, on to the next in the chain */
+ priv->p = priv->p->next;
+ } else {
+ /* p is a single pbuf, set it to NULL so next time a new
+ * pbuf is allocated */
+ priv->p = NULL;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#if !NO_SYS
+/**
+ * The SLIP input thread.
+ *
+ * Feed the IP layer with incoming packets
+ *
+ * @param nf the lwip network interface structure for this slipif
+ */
+static void
+slipif_loop_thread(void *nf)
+{
+ struct pbuf *p;
+ struct netif *netif = (struct netif *)nf;
+
+ while (1) {
+ p = slipif_input(netif, SLIP_BLOCK);
+ if (p != NULL) {
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ }
+}
+#endif /* !NO_SYS */
+
+/**
+ * SLIP netif initialization
+ *
+ * Call the arch specific sio_open and remember
+ * the opened device in the state field of the netif.
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @return ERR_OK if serial line could be opened,
+ * ERR_MEM if no memory could be allocated,
+ * ERR_IF is serial line couldn't be opened
+ *
+ * @note netif->num must contain the number of the serial port to open
+ * (0 by default)
+ */
+err_t
+slipif_init(struct netif *netif)
+{
+ struct slipif_priv *priv;
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num));
+
+ /* Allocate private data */
+ priv = mem_malloc(sizeof(struct slipif_priv));
+ if (!priv) {
+ return ERR_MEM;
+ }
+
+ netif->name[0] = 's';
+ netif->name[1] = 'l';
+ netif->output = slipif_output;
+ netif->mtu = SLIP_MAX_SIZE;
+ netif->flags |= NETIF_FLAG_POINTTOPOINT;
+
+ /* Try to open the serial port (netif->num contains the port number). */
+ priv->sd = sio_open(netif->num);
+ if (!priv->sd) {
+ /* Opening the serial port failed. */
+ mem_free(priv);
+ return ERR_IF;
+ }
+
+ /* Initialize private data */
+ priv->p = NULL;
+ priv->q = NULL;
+ priv->state = SLIP_RECV_NORMAL;
+ priv->i = 0;
+ priv->recved = 0;
+
+ netif->state = priv;
+
+ /* initialize the snmp variables and counters inside the struct netif
+ * ifSpeed: no assumption can be made without knowing more about the
+ * serial line!
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_slip, 0);
+
+ /* Create a thread to poll the serial line. */
+ sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif,
+ SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO);
+ return ERR_OK;
+}
+
+/**
+ * Polls the serial device and feeds the IP layer with incoming packets.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ */
+void
+slipif_poll(struct netif *netif)
+{
+ struct pbuf *p;
+ struct slipif_priv *priv;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = netif->state;
+
+ while ((p = slipif_input(netif, SLIP_DONTBLOCK)) != NULL) {
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ }
+}
+
+#endif /* LWIP_HAVE_SLIPIF */
diff --git a/core/lwip/src/netif/undiif.c b/core/lwip/src/netif/undiif.c
new file mode 100644
index 00000000..e62a984d
--- /dev/null
+++ b/core/lwip/src/netif/undiif.c
@@ -0,0 +1,1607 @@
+/**
+ * @file
+ * Ethernet Interface Skeleton
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Author: H. Peter Anvin <hpa@@zytor.com>
+ * Author: Eric Biederman <ebiederm@xmission.com>
+ *
+ */
+
+/*
+ * This file is a skeleton for developing Ethernet network interface
+ * drivers for lwIP. Add code to the low_level functions and do a
+ * search-and-replace for the word "ethernetif" to replace it with
+ * something that better describes your network interface.
+ */
+
+/* other headers include deprintf.h too early */
+#define UNDIIF_ID_FULL_DEBUG (UNDIIF_ID_DEBUG | UNDIIF_DEBUG)
+
+#if UNDIIF_ID_FULL_DEBUG
+# ifndef DEBUG
+# define DEBUG 1
+# endif
+# ifndef DEBUG_PORT
+# define DEBUG_PORT 0x3f8
+# endif
+#endif /* UNDIIF_ID_FULL_DEBUG */
+
+#include <core.h>
+
+#include "lwip/opt.h"
+
+#define LWIP_UNDIIF_DBG(debug) \
+ ( ((debug) & LWIP_DBG_ON) && \
+ ((debug) & LWIP_DBG_TYPES_ON) && \
+ (((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL) )
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include <lwip/stats.h>
+#include <lwip/snmp.h>
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+#include "lwip/netifapi.h"
+#include "lwip/tcpip.h"
+#include "../../../fs/pxe/pxe.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <syslinux/pxe_api.h>
+#include <dprintf.h>
+
+/* debug extras */
+#include "ipv4/lwip/icmp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/udp.h"
+
+#if LWIP_AUTOIP
+#error "AUTOIP not supported"
+#endif
+#if ETH_PAD_SIZE
+#error "ETH_PAD_SIZE not supported"
+#endif
+#if NETIF_MAX_HWADDR_LEN != MAC_MAX
+#error "hwaddr_len mismatch"
+#endif
+
+/** the time an ARP entry stays valid after its last update,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (240 * 5) seconds = 20 minutes.
+ */
+#define UNDIARP_MAXAGE 240
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (2 * 5) seconds = 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define UNDIARP_MAXPENDING 2
+
+typedef u8_t hwaddr_t[NETIF_MAX_HWADDR_LEN];
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** the ARP message */
+struct arp_hdr {
+ PACK_STRUCT_FIELD(u16_t hwtype);
+ PACK_STRUCT_FIELD(u16_t proto);
+ PACK_STRUCT_FIELD(u8_t hwlen);
+ PACK_STRUCT_FIELD(u8_t protolen);
+ PACK_STRUCT_FIELD(u16_t opcode);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+static inline int arp_hdr_len(struct netif *netif)
+{
+ return sizeof(struct arp_hdr) + (netif->hwaddr_len + sizeof(uint32_t))*2;
+}
+
+enum undiarp_state {
+ UNDIARP_STATE_EMPTY = 0,
+ UNDIARP_STATE_PENDING,
+ UNDIARP_STATE_STABLE
+};
+
+struct undiarp_entry {
+#if ARP_QUEUEING
+ /**
+ * Pointer to queue of pending outgoing packets on this ARP entry.
+ */
+ struct etharp_q_entry *q;
+#endif
+ struct ip_addr ipaddr;
+ u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
+ enum undiarp_state state;
+ u8_t ctime;
+ struct netif *netif;
+};
+
+#define PKTBUF_SIZE 2048
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'u'
+#define IFNAME1 'n'
+
+static struct netif undi_netif;
+static struct undiarp_entry arp_table[ARP_TABLE_SIZE];
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t undiarp_cached_entry;
+#endif
+
+/**
+ * Try hard to create a new entry - we want the IP address to appear in
+ * the cache (even if this means removing an active entry or so). */
+#define UNDIARP_TRY_HARD 1
+#define UNDIARP_FIND_ONLY 2
+
+#define UNIDIF_ID_STRLEN 300
+
+
+static inline bool undi_is_ethernet(struct netif *netif)
+{
+ (void)netif;
+ return MAC_type == ETHER_TYPE;
+}
+
+#if 0
+static void print_pbuf(struct pbuf *p)
+{
+ struct pbuf *q;
+ int off;
+
+ for( off = 0, q = p; q != NULL; q = q->next) {
+ unsigned char *byte, *end;
+ byte = q->payload;
+ end = byte + q->len;
+ for (; byte < end; byte++, off++ ) {
+ if ((off & 0xf) == 0) {
+ printf("%04x: ", off);
+ }
+ printf("%02x ", *byte);
+ if ((off & 0xf) == 0xf) {
+ printf("\n");
+ }
+ }
+ }
+ printf("\n");
+}
+#endif
+
+#if 0
+static void print_arp_pbuf(struct netif *netif, struct pbuf *p)
+{
+ struct arp_hdr *hdr;
+ u8_t *hdr_ptr;
+ int i;
+
+ hdr = p->payload;
+ hdr_ptr = (unsigned char *)(hdr + 1);
+ /* Fixed fields */
+ printf("arp: %04x %04x %04x %04x ",
+ hdr->hwtype,
+ hdr->proto,
+ hdr->_hwlen_protolen);
+ /* Source hardware address */
+ for(i = 0; i < netif->hwaddr_len; i++, hdr_ptr++) {
+ printf("%02x%c", *hdr_ptr,(i +1) == netif->hwaddr_len?' ':':');
+ }
+ /* Source ip address */
+ printf("%d.%d.%d.%d ", hdr_ptr[0], hdr_ptr[1], hdr_ptr[2], hdr_ptr[3]);
+ hdr_ptr += 4;
+ /* Destination hardware address */
+ for(i = 0; i < netif->hwaddr_len; i++, hdr_ptr++) {
+ printf("%02x%c", *hdr_ptr, (i +1) == netif->hwaddr_len?' ':':');
+ }
+ /* Destination ip address */
+ printf("%d.%d.%d.%d ", hdr_ptr[0], hdr_ptr[1], hdr_ptr[2], hdr_ptr[3]);
+ hdr_ptr += 4;
+}
+#endif
+
+#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
+int snprintf_eth_hdr(char *str, size_t size, char head[],
+ struct eth_hdr *ethhdr, char dir, char status,
+ char tail[])
+{
+ u8_t *d = ethhdr->dest.addr;
+ u8_t *s = ethhdr->src.addr;
+ return snprintf(str, size,
+ "%s: d:%02x:%02x:%02x:%02x:%02x:%02x"
+ " s:%02x:%02x:%02x:%02x:%02x:%02x"
+ " t:%4hx %c%c%s\n", head,
+ d[0], d[1], d[2], d[3], d[4], d[5],
+ s[0], s[1], s[2], s[3], s[4], s[5],
+ (unsigned)htons(ethhdr->type),
+ dir, status, tail);
+}
+
+int snprintf_arp_hdr(char *str, size_t size, char head[],
+ struct eth_hdr *ethhdr, char dir,
+ char status, char tail[])
+{
+ struct etharp_hdr *arphdr;
+ u8_t *d, *s;
+ struct ip_addr *sip, *dip;
+ if (ntohs(ethhdr->type) == ETHTYPE_ARP) {
+ arphdr = (struct etharp_hdr *)((void *)ethhdr + 14);
+ d = arphdr->dhwaddr.addr;
+ s = arphdr->shwaddr.addr;
+ sip = (struct ip_addr *) &(arphdr->sipaddr);
+ dip = (struct ip_addr *) &(arphdr->dipaddr);
+ return snprintf(str, size,
+ "%s: s:%02x:%02x:%02x:%02x:%02x:%02x"
+ " %3d.%3d.%3d.%3d"
+ " %02x:%02x:%02x:%02x:%02x:%02x"
+ " %3d.%3d.%3d.%3d"
+ " %c%c%s\n", head,
+ s[0], s[1], s[2], s[3], s[4], s[5],
+ ip4_addr1(sip), ip4_addr2(sip),
+ ip4_addr3(sip), ip4_addr4(sip),
+ d[0], d[1], d[2], d[3], d[4], d[5],
+ ip4_addr1(dip), ip4_addr2(dip),
+ ip4_addr3(dip), ip4_addr4(dip),
+ dir, status, tail);
+ } else {
+ return 0;
+ }
+}
+
+int snprintf_ip_hdr(char *str, size_t size, char head[],
+ struct eth_hdr *ethhdr, char dir,
+ char status, char tail[])
+{
+ struct ip_hdr *iphdr;
+ if (ntohs(ethhdr->type) == ETHTYPE_IP) {
+ iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
+ return snprintf(str, size,
+ "%s: s:%3d.%3d.%3d.%3d %3d.%3d.%3d.%3d l:%5d"
+ " i:%04x p:%04x c:%04x hl:%3d"
+ " %c%c%s\n", head,
+ ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src),
+ ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src),
+ ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest),
+ ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest),
+ ntohs(IPH_LEN(iphdr)), ntohs(IPH_ID(iphdr)),
+ IPH_PROTO(iphdr), ntohs(IPH_CHKSUM(iphdr)),
+ (IPH_HL(iphdr) << 2),
+ dir, status, tail);
+ } else {
+ return 0;
+ }
+}
+
+int snprintf_icmp_hdr(char *str, size_t size, char head[],
+ struct eth_hdr *ethhdr, char dir,
+ char status, char tail[])
+{
+ struct ip_hdr *iphdr;
+ struct icmp_echo_hdr *icmphdr;
+ if (ntohs(ethhdr->type) == ETHTYPE_IP) {
+ iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
+ if (IPH_PROTO(iphdr) == IP_PROTO_ICMP) {
+ icmphdr = (struct icmp_echo_hdr *)((void *)iphdr + (IPH_HL(iphdr) << 2));
+ return snprintf(str, size,
+ "%s: t:%02x c:%02x k:%04x"
+ " i:%04x s:%04x "
+ " %c%c%s\n", head,
+ icmphdr->type, icmphdr->code, ntohs(icmphdr->chksum),
+ ntohs(icmphdr->id), ntohs(icmphdr->seqno),
+ dir, status, tail);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+int snprintf_tcp_hdr(char *str, size_t size, char head[],
+ struct eth_hdr *ethhdr, char dir,
+ char status, char tail[])
+{
+ struct ip_hdr *iphdr;
+ struct tcp_hdr *tcphdr;
+ if (ntohs(ethhdr->type) == ETHTYPE_IP) {
+ iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
+ if (IPH_PROTO(iphdr) == IP_PROTO_TCP) {
+ tcphdr = (struct tcp_hdr *)((void *)iphdr + (IPH_HL(iphdr) << 2));
+ u16_t lenfl = ntohs(tcphdr->_hdrlen_rsvd_flags);
+ return snprintf(str, size,
+ "%s: s:%5d %5d q:%08x a:%08x lf:%04x k:%04x"
+ " %c%c%s\n", head,
+ ntohs(tcphdr->src), ntohs(tcphdr->dest),
+ ntohl(tcphdr->seqno), ntohl(tcphdr->ackno),
+ lenfl, ntohs(tcphdr->chksum),
+ dir, status, tail);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+int snprintf_udp_hdr(char *str, size_t size, char head[],
+ struct eth_hdr *ethhdr, char dir,
+ char status, char tail[])
+{
+ struct ip_hdr *iphdr;
+ struct udp_hdr *udphdr;
+ if (ntohs(ethhdr->type) == ETHTYPE_IP) {
+ iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ udphdr = (struct udp_hdr *)((void *)iphdr + (IPH_HL(iphdr) << 2));
+ return snprintf(str, size,
+ "%s: s:%5d %5d l:%d c:%04x"
+ " %c%c%s\n", head,
+ ntohs(udphdr->src), ntohs(udphdr->dest),
+ ntohs(udphdr->len), ntohs(udphdr->chksum),
+ dir, status, tail);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+#endif /* UNDIIF_ID_FULL_DEBUG */
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from undiif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ * for this undiif
+ */
+static void
+low_level_init(struct netif *netif)
+{
+ static __lowmem t_PXENV_UNDI_OPEN undi_open;
+ int i;
+
+ /* MAC_type and MAC_len should always match what is returned by
+ * PXENV_UNDI_GET_INFORMATION. At the moment the both seem to be
+ * reliable but if they disagree that is a sign of a nasty bug
+ * somewhere so abort.
+ */
+ /* If we are in conflict abort */
+ if (MAC_type != pxe_undi_info.HwType) {
+ printf("HwType conflicit: %u != %u\n",
+ MAC_type, pxe_undi_info.HwType);
+ kaboom();
+ }
+ if (MAC_len != pxe_undi_info.HwAddrLen) {
+ printf("HwAddrLen conflict: %u != %u\n",
+ MAC_len, pxe_undi_info.HwAddrLen);
+ kaboom();
+ }
+
+ /* set MAC hardware address length */
+ netif->hwaddr_len = MAC_len;
+
+ /* set MAC hardware address */
+ memcpy(netif->hwaddr, MAC, MAC_len);
+
+ /* maximum transfer unit */
+ netif->mtu = pxe_undi_info.MaxTranUnit;
+
+ dprintf("UNDI: hw address");
+ for (i = 0; i < netif->hwaddr_len; i++)
+ dprintf("%c%02x", i ? ':' : ' ', (uint8_t)netif->hwaddr[i]);
+ dprintf("\n");
+
+ /* device capabilities */
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_LINK_UP;
+ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+ if (undi_is_ethernet(netif))
+ netif->flags |= NETIF_FLAG_ETHARP;
+
+ /* Install the interrupt vector */
+ pxe_start_isr();
+
+ /* Open the UNDI stack - you'd think the BC would have done this... */
+ undi_open.PktFilter = 0x0003; /* FLTR_DIRECTED | FLTR_BRDCST */
+ pxe_call(PXENV_UNDI_OPEN, &undi_open);
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this undiif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ * an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ * strange results. You might consider waiting for space in the DMA queue
+ * to become availale since the stack doesn't retry to send a packet
+ * dropped because of memory failure (except for the TCP timers).
+ */
+extern volatile uint32_t pxe_irq_count;
+extern volatile uint8_t pxe_need_poll;
+
+static err_t
+undi_transmit(struct netif *netif, struct pbuf *pbuf,
+ hwaddr_t *dest, uint16_t undi_protocol)
+{
+ struct pxe_xmit {
+ t_PXENV_UNDI_TRANSMIT xmit;
+ t_PXENV_UNDI_TBD tbd;
+ };
+ static __lowmem struct pxe_xmit pxe;
+ static __lowmem hwaddr_t low_dest;
+ static __lowmem char pkt_buf[PKTBUF_SIZE];
+ uint32_t now;
+ static uint32_t first_xmit;
+#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
+ char *str = malloc(UNIDIF_ID_STRLEN);
+ int strpos = 0;
+ struct eth_hdr *ethhdr = pbuf->payload;
+
+
+ strpos += snprintf(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ "undi xmit thd '%s'\n", current()->name);
+ strpos += snprintf_eth_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ "undi", ethhdr, 'x', '0', "");
+ strpos += snprintf_arp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " arp", ethhdr, 'x', '0', "");
+ strpos += snprintf_ip_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " ip", ethhdr, 'x', '0', "");
+ strpos += snprintf_icmp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " icmp", ethhdr, 'x', '0', "");
+ strpos += snprintf_tcp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " tcp", ethhdr, 'x', '0', "");
+ strpos += snprintf_udp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " udp", ethhdr, 'x', '0', "");
+ LWIP_DEBUGF(UNDIIF_ID_FULL_DEBUG, ("%s", str));
+ free(str);
+#endif /* UNDIIF_ID_FULL_DEBUG */
+
+ /* Drop jumbo frames */
+ if ((pbuf->tot_len > sizeof(pkt_buf)) || (pbuf->tot_len > netif->mtu))
+ return ERR_ARG;
+
+ if (__unlikely(!pxe_irq_count)) {
+ now = ms_timer();
+ if (!first_xmit) {
+ first_xmit = now;
+ } else if (now - first_xmit > 3000) {
+ /* 3 seconds after first transmit, and no interrupts */
+ LWIP_PLATFORM_DIAG(("undiif: forcing polling\n"));
+ asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
+ asm volatile("incl %0" : "+m" (pxe_irq_count));
+ }
+ }
+
+ pbuf_copy_partial( pbuf, pkt_buf, pbuf->tot_len, 0);
+ if (dest)
+ memcpy(low_dest, dest, netif->hwaddr_len);
+
+ do {
+ memset(&pxe, 0, sizeof pxe);
+
+ pxe.xmit.Protocol = undi_protocol;
+ pxe.xmit.XmitFlag = dest? XMT_DESTADDR : XMT_BROADCAST;
+ pxe.xmit.DestAddr = FAR_PTR(&low_dest);
+ pxe.xmit.TBD = FAR_PTR(&pxe.tbd);
+ pxe.tbd.ImmedLength = pbuf->tot_len;
+ pxe.tbd.Xmit = FAR_PTR(pkt_buf);
+
+ pxe_call(PXENV_UNDI_TRANSMIT, &pxe.xmit);
+ } while (pxe.xmit.Status == PXENV_STATUS_OUT_OF_RESOURCES);
+
+ LINK_STATS_INC(link.xmit);
+
+ return ERR_OK;
+}
+
+static err_t
+undi_send_unknown(struct netif *netif, struct pbuf *pbuf)
+{
+ return undi_transmit(netif, pbuf, NULL, P_UNKNOWN);
+}
+
+static err_t
+undi_send_ip(struct netif *netif, struct pbuf *pbuf, hwaddr_t *dst)
+{
+ return undi_transmit(netif, pbuf, dst, P_IP);
+}
+
+static err_t
+undi_send_arp(struct netif *netif, struct pbuf *pbuf, hwaddr_t *dst)
+{
+ return undi_transmit(netif, pbuf, dst, P_ARP);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+undiarp_request(struct netif *netif, struct ip_addr *ipaddr)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct arp_hdr *hdr;
+ u8_t *hdr_ptr;
+
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_request: sending ARP request.\n"));
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_RAW, arp_hdr_len(netif), PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("undiarp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold arp_hdr_len bytesr",
+ (p->len >= arp_hdr_len(netif)));
+
+ hdr = p->payload;
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_request: sending raw ARP packet.\n"));
+ hdr->opcode = htons(ARP_REQUEST);
+ hdr->hwtype = htons(MAC_type);
+ hdr->proto = htons(ETHTYPE_IP);
+ hdr->hwlen = netif->hwaddr_len;
+ hdr->protolen = sizeof(struct ip_addr);
+
+ hdr_ptr = (unsigned char *)(hdr + 1);
+ memcpy(hdr_ptr, netif->hwaddr, netif->hwaddr_len);
+ hdr_ptr += netif->hwaddr_len;
+ memcpy(hdr_ptr, &netif->ip_addr, 4);
+ hdr_ptr += 4;
+ memset(hdr_ptr, 0, netif->hwaddr_len);
+ hdr_ptr += netif->hwaddr_len;
+ memcpy(hdr_ptr, ipaddr, 4);
+
+ /* send ARP query */
+ result = undi_send_arp(netif, p, NULL);
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_undiarp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#endif
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ETHARP_TMR_INTERVAL microseconds (5 seconds),
+ * in order to expire entries in the ARP table.
+ */
+void
+undiarp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG, ("undiarp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ arp_table[i].ctime++;
+ if (((arp_table[i].state == UNDIARP_STATE_STABLE) &&
+ (arp_table[i].ctime >= UNDIARP_MAXAGE)) ||
+ ((arp_table[i].state == UNDIARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= UNDIARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG , ("undiarp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state == UNDIARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ /* remove from SNMP ARP index tree */
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+#if ARP_QUEUEING
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG , ("undiarp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_undiarp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+#endif
+ /* recycle entry for re-use */
+ arp_table[i].state = UNDIARP_STATE_EMPTY;
+ }
+#if ARP_QUEUEING
+ /* still pending entry? (not expired) */
+ if (arp_table[i].state == UNDIARP_STATE_PENDING) {
+ /* resend an ARP query here? */
+ }
+#endif
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and UNDIARP_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags
+ * - UNDIARP_TRY_HARD: Try hard to create a entry by allowing recycling of
+ * active (stable or pending) entries.
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+#if LWIP_NETIF_HWADDRHINT
+find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif)
+#else /* LWIP_NETIF_HWADDRHINT */
+find_entry(struct ip_addr *ipaddr, u8_t flags)
+#endif /* LWIP_NETIF_HWADDRHINT */
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0, age_pending = 0, age_stable = 0;
+#if ARP_QUEUEING
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u8_t age_queue = 0;
+#endif
+
+ /* First, test if the last call to this function asked for the
+ * same address. If so, we're really fast! */
+ if (ipaddr) {
+ /* ipaddr to search for was given */
+#if LWIP_NETIF_HWADDRHINT
+ if ((netif != NULL) && (netif->addr_hint != NULL)) {
+ /* per-pcb cached entry was given */
+ u8_t per_pcb_cache = *(netif->addr_hint);
+ if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == UNDIARP_STATE_STABLE) {
+ /* the per-pcb-cached entry is stable */
+ if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) {
+ /* per-pcb cached entry was the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return per_pcb_cache;
+ }
+ }
+ }
+#else /* #if LWIP_NETIF_HWADDRHINT */
+ if (arp_table[undiarp_cached_entry].state == UNDIARP_STATE_STABLE) {
+ /* the cached entry is stable */
+ if (ip_addr_cmp(ipaddr, &arp_table[undiarp_cached_entry].ipaddr)) {
+ /* cached entry was the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return undiarp_cached_entry;
+ }
+ }
+#endif /* #if LWIP_NETIF_HWADDRHINT */
+ }
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == UNDIARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG , ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ }
+ /* pending entry? */
+ else if (arp_table[i].state == UNDIARP_STATE_PENDING) {
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+#if LWIP_NETIF_HWADDRHINT
+ NETIF_SET_HINT(netif, i);
+#else /* #if LWIP_NETIF_HWADDRHINT */
+ undiarp_cached_entry = i;
+#endif /* #if LWIP_NETIF_HWADDRHINT */
+ return i;
+#if ARP_QUEUEING
+ /* pending with queued packets? */
+ } else if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+#endif
+ /* pending without queued packets? */
+ } else {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ }
+ /* stable entry? */
+ else if (arp_table[i].state == UNDIARP_STATE_STABLE) {
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+#if LWIP_NETIF_HWADDRHINT
+ NETIF_SET_HINT(netif, i);
+#else /* #if LWIP_NETIF_HWADDRHINT */
+ undiarp_cached_entry = i;
+#endif /* #if LWIP_NETIF_HWADDRHINT */
+ return i;
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ } else if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* no empty entry found and not allowed to recycle? */
+ if (((empty == ARP_TABLE_SIZE) && ((flags & UNDIARP_TRY_HARD) == 0))
+ /* or don't create new entry, only search? */
+ || ((flags & UNDIARP_FIND_ONLY) != 0)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { UNDIARP_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ }
+ /* 2) found recyclable stable entry? */
+ else if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+#if ARP_QUEUEING
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+#endif
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+#if ARP_QUEUEING
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_queue;
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_undiarp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+#endif
+ /* no empty or recyclable entries found */
+ } else {
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+
+ if (arp_table[i].state != UNDIARP_STATE_EMPTY)
+ {
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+ }
+ /* recycle entry (no-op for an already empty entry) */
+ arp_table[i].state = UNDIARP_STATE_EMPTY;
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip_addr_set(&arp_table[i].ipaddr, ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if LWIP_NETIF_HWADDRHINT
+ NETIF_SET_HINT(netif, i);
+#else /* #if LWIP_NETIF_HWADDRHINT */
+ undiarp_cached_entry = i;
+#endif /* #if LWIP_NETIF_HWADDRHINT */
+ return (err_t)i;
+}
+
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+static err_t
+undiarp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
+{
+ err_t result = ERR_MEM;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr) ||
+ ip_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+#if LWIP_NETIF_HWADDRHINT
+ i = find_entry(ipaddr, UNDIARP_TRY_HARD, netif);
+#else /* LWIP_NETIF_HWADDRHINT */
+ i = find_entry(ipaddr, UNDIARP_TRY_HARD);
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == UNDIARP_STATE_EMPTY) {
+ arp_table[i].state = UNDIARP_STATE_PENDING;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == UNDIARP_STATE_PENDING) ||
+ (arp_table[i].state == UNDIARP_STATE_STABLE)));
+
+ /* do we have a pending entry? or an implicit query request? */
+ if ((arp_table[i].state == UNDIARP_STATE_PENDING) || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = undiarp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in undiarp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ }
+
+ /* packet given? */
+ if (q != NULL) {
+ /* stable entry? */
+ if (arp_table[i].state == UNDIARP_STATE_STABLE) {
+ /* we have a valid IP->hardware address mapping */
+ /* send the packet */
+ result = undi_send_ip(netif, q, &(arp_table[i].hwaddr));
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == UNDIARP_STATE_PENDING) {
+#if ARP_QUEUEING /* queue the given q packet */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ new_entry->next = 0;
+ new_entry->p = p;
+ if(arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ while (r->next != NULL) {
+ r = r->next;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ /* { result == ERR_MEM } through initialization */
+ }
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ /* { result == ERR_MEM } through initialization */
+ }
+#else /* ARP_QUEUEING == 0 */
+ /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */
+ /* { result == ERR_MEM } through initialization */
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q));
+#endif
+ }
+ }
+ return result;
+}
+
+/**
+ * Resolve and fill-in address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+static err_t
+undiarp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr)
+{
+ static __lowmem t_PXENV_UNDI_GET_MCAST_ADDR get_mcast;
+ hwaddr_t *dest;
+
+ if (undi_is_ethernet(netif))
+ return etharp_output(netif, q, ipaddr);
+
+ /* Assume unresolved hardware address */
+ dest = NULL;
+
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table.
+ */
+ if (ip_addr_isbroadcast(ipaddr, netif)) {
+ dest = NULL;
+ }
+ else if (ip_addr_ismulticast(ipaddr)) {
+ memset(&get_mcast, 0, sizeof get_mcast);
+ memcpy(&get_mcast.InetAddr, ipaddr, sizeof(get_mcast.InetAddr));
+ pxe_call(PXENV_UNDI_GET_MCAST_ADDR, &get_mcast);
+ dest = (hwaddr_t *)&get_mcast.MediaAddr;
+ }
+ else {
+ /* outside local network? */
+ if (!ip_addr_netcmp(ipaddr, &netif->ip_addr, &netif->netmask)) {
+ /* interface has default gateway? */
+ if (netif->gw.addr != 0) {
+ /* send to hardware address of default gateway IP address */
+ ipaddr = &(netif->gw);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ /* queue on destination Ethernet address belonging to ipaddr */
+ return undiarp_query(netif, ipaddr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return undi_send_ip(netif, q, dest);
+}
+
+static void get_packet_fragment(t_PXENV_UNDI_ISR *isr)
+{
+ do {
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR, &isr);
+ } while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE);
+}
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this undiif
+ * @return a pbuf filled with the received packet (including MAC header)
+ * NULL on memory error
+ */
+static struct pbuf *
+low_level_input(t_PXENV_UNDI_ISR *isr)
+{
+ struct pbuf *p, *q;
+ const char *r;
+ int len;
+
+ /* Obtain the size of the packet and put it into the "len"
+ variable. */
+ len = isr->FrameLength;
+
+ //printf("undiif_input, len = %d\n", len);
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+
+ if (p != NULL) {
+ /*
+ * We iterate over the pbuf chain until we have read the entire
+ * packet into the pbuf.
+ */
+ r = GET_PTR(isr->Frame);
+ for (q = p; q != NULL; q = q->next) {
+ /*
+ * Read enough bytes to fill this pbuf in the chain. The
+ * available data in the pbuf is given by the q->len
+ * variable.
+ */
+ char *s = q->payload;
+ int ql = q->len;
+
+ while (ql) {
+ int qb = isr->BufferLength < ql ? isr->BufferLength : ql;
+
+ if (!qb) {
+ /*
+ * Only received a partial frame, must get the next one...
+ */
+ get_packet_fragment(isr);
+ r = GET_PTR(isr->Frame);
+ } else {
+ memcpy(s, r, qb);
+ s += qb;
+ r += qb;
+ ql -= qb;
+ }
+ }
+ }
+
+ LINK_STATS_INC(link.recv);
+ } else {
+ /*
+ * Dropped packet: we really should make sure we drain any partial
+ * frame here...
+ */
+ while ((len -= isr->BufferLength) > 0)
+ get_packet_fragment(isr);
+
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ }
+
+ return p;
+}
+
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags Defines behaviour:
+ * - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified,
+ * only existing ARP entries will be updated.
+ *
+ * @return
+ * - ERR_OK Succesfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+update_arp_entry(struct netif *netif, struct ip_addr *ipaddr,
+ hwaddr_t *lladdr, u8_t flags)
+{
+ s8_t i;
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry()\n"));
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr),
+ (*lladdr)[0], (*lladdr)[1], (*lladdr)[2],
+ (*lladdr)[3], (*lladdr)[4], (*lladdr)[5]));
+ /* non-unicast address? */
+ if (ip_addr_isany(ipaddr) ||
+ ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+#if LWIP_NETIF_HWADDRHINT
+ i = find_entry(ipaddr, flags, netif);
+#else /* LWIP_NETIF_HWADDRHINT */
+ i = find_entry(ipaddr, flags);
+#endif /* LWIP_NETIF_HWADDRHINT */
+ /* bail out if no entry could be found */
+ if (i < 0)
+ return (err_t)i;
+
+ /* mark it stable */
+ arp_table[i].state = UNDIARP_STATE_STABLE;
+ /* record network interface */
+ arp_table[i].netif = netif;
+
+ /* insert in SNMP ARP index tree */
+ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ memcpy(arp_table[i].hwaddr, lladdr, netif->hwaddr_len);
+
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+#if ARP_QUEUEING
+ /* this is where we will send out queued packets! */
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+ /* send the queued IP packet */
+ undi_send_ip(netif, p, lladdr);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+#endif
+ return ERR_OK;
+}
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ * @param ethaddr Ethernet address of netif.
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+undiarp_input(struct netif *netif, struct pbuf *p)
+{
+ struct arp_hdr *hdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ struct ip_addr sipaddr, dipaddr;
+ hwaddr_t hwaddr_remote;
+ u8_t *hdr_ptr;
+ u8_t for_us;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
+ since a struct arp_hdr is pointed to p->payload, so it musn't be chained! */
+ if (p->len < arp_hdr_len(netif)) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("undiarp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
+ (s16_t)SIZEOF_ETHARP_PACKET));
+ printf("short arp packet\n");
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+
+ hdr = p->payload;
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != htons(MAC_type)) ||
+ (hdr->hwlen != netif->hwaddr_len) ||
+ (hdr->protolen != sizeof(struct ip_addr)) ||
+ (hdr->proto != htons(ETHTYPE_IP))) {
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("undiarp_input: packet dropped, wrong hw type, hwlen, proto, or protolen (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ printf("malformed arp packet\n");
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ hdr_ptr = (unsigned char *)(hdr + 1);
+ memcpy(hwaddr_remote, hdr_ptr, netif->hwaddr_len);
+ hdr_ptr += netif->hwaddr_len;
+ memcpy(&sipaddr, hdr_ptr, sizeof(sipaddr));
+ hdr_ptr += sizeof(sipaddr);
+ hdr_ptr += netif->hwaddr_len;
+ memcpy(&dipaddr, hdr_ptr, sizeof(dipaddr));
+
+ /* this interface is not configured? */
+ if (netif->ip_addr.addr == 0) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+ }
+
+ /* ARP message directed to us? */
+ if (for_us) {
+ /* add IP address in ARP cache; assume requester wants to talk to us.
+ * can result in directly sending the queued packets for this host. */
+ update_arp_entry(netif, &sipaddr, &hwaddr_remote, UNDIARP_TRY_HARD);
+ /* ARP message not directed to us? */
+ } else {
+ /* update the source IP address in the cache, if present */
+ update_arp_entry(netif, &sipaddr, &hwaddr_remote, 0);
+ }
+
+ /* now act on the message itself */
+ switch (htons(hdr->opcode)) {
+ /* ARP request? */
+ case ARP_REQUEST:
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possiby send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: replying to ARP request for our IP address\n"));
+ /* Re-use pbuf to send ARP reply.
+ Since we are re-using an existing pbuf, we can't call etharp_raw since
+ that would allocate a new pbuf. */
+ hdr->opcode = htons(ARP_REPLY);
+ hdr_ptr = (unsigned char *)(hdr + 1);
+ memcpy(hdr_ptr, &netif->hwaddr, netif->hwaddr_len);
+ hdr_ptr += netif->hwaddr_len;
+ memcpy(hdr_ptr, &dipaddr, sizeof(dipaddr));
+ hdr_ptr += sizeof(dipaddr);
+ memcpy(hdr_ptr, &hwaddr_remote, netif->hwaddr_len);
+ hdr_ptr += netif->hwaddr_len;
+ memcpy(hdr_ptr, &sipaddr, sizeof(sipaddr));
+
+ /* return ARP reply */
+ undi_send_arp(netif, p, &hwaddr_remote);
+ /* we are not configured? */
+ } else if (netif->ip_addr.addr == 0) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case ARP_REPLY:
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif
+ break;
+ default:
+ LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this undiif
+ */
+void undiif_input(t_PXENV_UNDI_ISR *isr)
+{
+ struct pbuf *p;
+ u8_t undi_prot;
+ u16_t llhdr_len;
+
+ /* From the first isr capture the essential information */
+ undi_prot = isr->ProtType;
+ llhdr_len = isr->FrameHeaderLength;
+
+ /* move received packet into a new pbuf */
+ p = low_level_input(isr);
+ /* no packet could be read, silently ignore this */
+ if (p == NULL) return;
+
+ if (undi_is_ethernet(&undi_netif)) {
+ /* points to packet payload, which starts with an Ethernet header */
+ struct eth_hdr *ethhdr = p->payload;
+#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
+ char *str = malloc(UNIDIF_ID_STRLEN);
+ int strpos = 0;
+
+ strpos += snprintf(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ "undi recv thd '%s'\n", current()->name);
+ strpos += snprintf_eth_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ "undi", ethhdr, 'r', '0', "");
+ strpos += snprintf_arp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " arp", ethhdr, 'r', '0', "");
+ strpos += snprintf_ip_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " ip", ethhdr, 'r', '0', "");
+ strpos += snprintf_icmp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " icmp", ethhdr, 'r', '0', "");
+ strpos += snprintf_tcp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " tcp", ethhdr, 'r', '0', "");
+ strpos += snprintf_udp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
+ " udp", ethhdr, 'r', '0', "");
+ LWIP_DEBUGF(UNDIIF_ID_FULL_DEBUG, ("%s", str));
+ free(str);
+#endif /* UNDIIF_ID_FULL_DEBUG */
+
+ switch (htons(ethhdr->type)) {
+ /* IP or ARP packet? */
+ case ETHTYPE_IP:
+ case ETHTYPE_ARP:
+#if PPPOE_SUPPORT
+ /* PPPoE packet? */
+ case ETHTYPE_PPPOEDISC:
+ case ETHTYPE_PPPOE:
+#endif /* PPPOE_SUPPORT */
+ /* full packet send to tcpip_thread to process */
+ if (tcpip_input(p, &undi_netif)!=ERR_OK)
+ { LWIP_DEBUGF(UNDIIF_NET_DEBUG | UNDIIF_DEBUG, ("undiif_input: IP input error\n"));
+ pbuf_free(p);
+ p = NULL;
+ }
+ break;
+
+ default:
+ pbuf_free(p);
+ p = NULL;
+ break;
+ }
+ } else {
+ if (pbuf_header(p, -(s16_t)llhdr_len)) {
+ LWIP_ASSERT("Can't move link level header in packet", 0);
+ pbuf_free(p);
+ p = NULL;
+ } else {
+ switch(undi_prot) {
+ case P_IP:
+ /* pass to IP layer */
+ tcpip_input(p, &undi_netif);
+ break;
+
+ case P_ARP:
+ /* pass p to ARP module */
+ undiarp_input(&undi_netif, p);
+ break;
+
+ default:
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ p = NULL;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this undiif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ * any other err_t on error
+ */
+static err_t
+undiif_init(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+#if LWIP_NETIF_HOSTNAME
+ /* Initialize interface hostname */
+ netif->hostname = "undi";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ /*
+ * Initialize the snmp variables and counters inside the struct netif.
+ * The last argument should be replaced with your link speed, in units
+ * of bits per second.
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+
+ netif->state = NULL; /* Private pointer if we need it */
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+ netif->output = undiarp_output;
+ netif->linkoutput = undi_send_unknown;
+
+ /* initialize the hardware */
+ low_level_init(netif);
+
+ return ERR_OK;
+}
+
+int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw)
+{
+ err_t err;
+
+ // This should be done *after* the threading system and receive thread
+ // have both been started.
+ dprintf("undi_netif: ip %d.%d.%d.%d netmask %d.%d.%d.%d gw %d.%d.%d.%d\n",
+ ((uint8_t *)&ip)[0],
+ ((uint8_t *)&ip)[1],
+ ((uint8_t *)&ip)[2],
+ ((uint8_t *)&ip)[3],
+ ((uint8_t *)&netmask)[0],
+ ((uint8_t *)&netmask)[1],
+ ((uint8_t *)&netmask)[2],
+ ((uint8_t *)&netmask)[3],
+ ((uint8_t *)&gw)[0],
+ ((uint8_t *)&gw)[1],
+ ((uint8_t *)&gw)[2],
+ ((uint8_t *)&gw)[3]);
+ err = netifapi_netif_add(&undi_netif,
+ (struct ip_addr *)&ip, (struct ip_addr *)&netmask, (struct ip_addr *)&gw,
+ NULL, undiif_init, tcpip_input);
+ if (err)
+ return err;
+
+ netif_set_up(&undi_netif);
+ netif_set_default(&undi_netif); /* Make this interface the default route */
+
+ return ERR_OK;
+}
diff --git a/core/lzo/enter.ash b/core/lzo/enter.ash
index 49c455d6..c2aa0817 100644
--- a/core/lzo/enter.ash
+++ b/core/lzo/enter.ash
@@ -2,6 +2,9 @@
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
diff --git a/core/lzo/leave.ash b/core/lzo/leave.ash
index 9550b46f..0fdb729f 100644
--- a/core/lzo/leave.ash
+++ b/core/lzo/leave.ash
@@ -2,6 +2,9 @@
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
diff --git a/core/lzo/lzo1c_d.ash b/core/lzo/lzo1c_d.ash
index 9969c86a..56622ab5 100644
--- a/core/lzo/lzo1c_d.ash
+++ b/core/lzo/lzo1c_d.ash
@@ -2,6 +2,9 @@
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
diff --git a/core/lzo/lzo1f_d.ash b/core/lzo/lzo1f_d.ash
index aa9279e0..97a103c0 100644
--- a/core/lzo/lzo1f_d.ash
+++ b/core/lzo/lzo1f_d.ash
@@ -2,6 +2,9 @@
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
diff --git a/core/lzo/lzo1x_d.ash b/core/lzo/lzo1x_d.ash
index aa138354..782b47fe 100644
--- a/core/lzo/lzo1x_d.ash
+++ b/core/lzo/lzo1x_d.ash
@@ -2,6 +2,9 @@
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
diff --git a/core/lzo/lzo1x_f1.S b/core/lzo/lzo1x_f2.S
index 8360e34a..8cc26b8b 100644
--- a/core/lzo/lzo1x_f1.S
+++ b/core/lzo/lzo1x_f2.S
@@ -1,7 +1,10 @@
-/* lzo1x_f1.S -- fast LZO1X decompression in assembler (i386 + gcc)
+/* lzo1x_f2.S -- fast LZO1X decompression in assembler (i386 + gcc)
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
@@ -44,17 +47,21 @@
#define LZO_FAST
+#define LZO_TEST_DECOMPRESS_OVERRUN_INPUT
+#define LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT
+#define LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND
+
#include "lzo_asm.h"
.section ".textnr","ax"
- LZO_PUBLIC(lzo1x_decompress_asm_fast)
+ LZO_PUBLIC(lzo1x_decompress_asm_fast_safe)
#include "enter.ash"
#include "lzo1x_d.ash"
#include "leave.ash"
- LZO_PUBLIC_END(lzo1x_decompress_asm_fast)
+ LZO_PUBLIC_END(lzo1x_decompress_asm_fast_safe)
/*
diff --git a/core/lzo/lzo_asm.h b/core/lzo/lzo_asm.h
index 55fdf6d1..1188dd60 100644
--- a/core/lzo/lzo_asm.h
+++ b/core/lzo/lzo_asm.h
@@ -2,6 +2,9 @@
This file is part of the LZO real-time data compression library.
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
@@ -52,17 +55,17 @@
#else
/* manual configuration - see defaults below */
# if defined(__ELF__)
-# define MFX_ASM_HAVE_TYPE
-# define MFX_ASM_NAME_NO_UNDERSCORES
+# define MFX_ASM_HAVE_TYPE 1
+# define MFX_ASM_NAME_NO_UNDERSCORES 1
# elif defined(__linux__) /* Linux a.out */
-# define MFX_ASM_ALIGN_PTWO
+# define MFX_ASM_ALIGN_PTWO 1
# elif defined(__DJGPP__)
-# define MFX_ASM_ALIGN_PTWO
+# define MFX_ASM_ALIGN_PTWO 1
# elif defined(__GO32__) /* djgpp v1 */
-# define MFX_ASM_CANNOT_USE_EBP
+# define MFX_ASM_CANNOT_USE_EBP 1
# elif defined(__EMX__)
-# define MFX_ASM_ALIGN_PTWO
-# define MFX_ASM_CANNOT_USE_EBP
+# define MFX_ASM_ALIGN_PTWO 1
+# define MFX_ASM_CANNOT_USE_EBP 1
# endif
#endif
#endif
@@ -123,7 +126,7 @@
************************************************************************/
#if !defined(MFX_ASM_ALIGN_BYTES) && !defined(MFX_ASM_ALIGN_PTWO)
-# define MFX_ASM_ALIGN_BYTES
+# define MFX_ASM_ALIGN_BYTES 1
#endif
#if !defined(LZO_ASM_ALIGN)
@@ -145,10 +148,10 @@
#if !defined(MFX_ASM_CANNOT_USE_EBP)
# if 1 && !defined(N_3_EBP) && !defined(N_255_EBP)
-# define N_3_EBP
+# define N_3_EBP 1
# endif
# if 0 && !defined(N_3_EBP) && !defined(N_255_EBP)
-# define N_255_EBP
+# define N_255_EBP 1
# endif
#endif
diff --git a/core/macros.inc b/core/macros.inc
index e3aedca1..c8fbe8de 100644
--- a/core/macros.inc
+++ b/core/macros.inc
@@ -31,6 +31,11 @@
%endif
%ifdef IS_PXELINUX
%define MY_NAME 'PXELINUX'
+ %if IS_LPXELINUX > 0
+ %define MY_TYPE 'lwIP'
+ %else
+ %define MY_TYPE 'PXE'
+ %endif
%else
%define IS_PXELINUX 0
%endif
diff --git a/core/mem/free.c b/core/mem/free.c
index 384f10ea..d7f912b1 100644
--- a/core/mem/free.c
+++ b/core/mem/free.c
@@ -8,12 +8,14 @@
#include <dprintf.h>
#include "malloc.h"
+#include <stdio.h>
+
static struct free_arena_header *
__free_block(struct free_arena_header *ah)
{
struct free_arena_header *pah, *nah;
struct free_arena_header *head =
- &__malloc_head[ARENA_HEAP_GET(ah->a.attrs)];
+ &__core_malloc_head[ARENA_HEAP_GET(ah->a.attrs)];
pah = ah->a.prev;
nah = ah->a.next;
@@ -64,7 +66,7 @@ __free_block(struct free_arena_header *ah)
return ah;
}
-void free(void *ptr)
+__export void free(void *ptr)
{
struct free_arena_header *ah;
@@ -77,10 +79,16 @@ void free(void *ptr)
((struct arena_header *)ptr - 1);
#ifdef DEBUG_MALLOC
- assert( ARENA_TYPE_GET(ah->a.attrs) == ARENA_TYPE_USED );
+ if (ah->a.magic != ARENA_MAGIC)
+ dprintf("failed free() magic check: %p\n", ptr);
+
+ if (ARENA_TYPE_GET(ah->a.attrs) != ARENA_TYPE_USED)
+ dprintf("invalid arena type: %d\n", ARENA_TYPE_GET(ah->a.attrs));
#endif
+ sem_down(&__malloc_semaphore, 0);
__free_block(ah);
+ sem_up(&__malloc_semaphore);
/* Here we could insert code to return memory to the system. */
}
@@ -93,7 +101,7 @@ void free(void *ptr)
void __inject_free_block(struct free_arena_header *ah)
{
struct free_arena_header *head =
- &__malloc_head[ARENA_HEAP_GET(ah->a.attrs)];
+ &__core_malloc_head[ARENA_HEAP_GET(ah->a.attrs)];
struct free_arena_header *nah;
size_t a_end = (size_t) ah + ARENA_SIZE_GET(ah->a.attrs);
size_t n_end;
@@ -102,6 +110,8 @@ void __inject_free_block(struct free_arena_header *ah)
ARENA_SIZE_GET(ah->a.attrs), ah,
ARENA_HEAP_GET(ah->a.attrs), head);
+ sem_down(&__malloc_semaphore, 0);
+
for (nah = head->a.next ; nah != head ; nah = nah->a.next) {
n_end = (size_t) nah + ARENA_SIZE_GET(nah->a.attrs);
@@ -113,7 +123,10 @@ void __inject_free_block(struct free_arena_header *ah)
if ((size_t) ah >= n_end)
continue;
+ printf("conflict:ah: %p, a_end: %p, nah: %p, n_end: %p\n", ah, a_end, nah, n_end);
+
/* Otherwise we have some sort of overlap - reject this block */
+ sem_up(&__malloc_semaphore);
return;
}
@@ -124,6 +137,8 @@ void __inject_free_block(struct free_arena_header *ah)
ah->a.prev->a.next = ah;
__free_block(ah);
+
+ sem_up(&__malloc_semaphore);
}
/*
@@ -133,9 +148,11 @@ static void __free_tagged(malloc_tag_t tag) {
struct free_arena_header *fp, *head;
int i;
+ sem_down(&__malloc_semaphore, 0);
+
for (i = 0; i < NHEAP; i++) {
dprintf("__free_tagged(%u) heap %d\n", tag, i);
- head = &__malloc_head[i];
+ head = &__core_malloc_head[i];
for (fp = head->a.next ; fp != head ; fp = fp->a.next) {
if (ARENA_TYPE_GET(fp->a.attrs) == ARENA_TYPE_USED &&
fp->a.tag == tag)
@@ -143,6 +160,7 @@ static void __free_tagged(malloc_tag_t tag) {
}
}
+ sem_up(&__malloc_semaphore);
dprintf("__free_tagged(%u) done\n", tag);
}
diff --git a/core/mem/init.c b/core/mem/init.c
index 487bbb3f..d6a5f189 100644
--- a/core/mem/init.c
+++ b/core/mem/init.c
@@ -2,37 +2,101 @@
#include <errno.h>
#include <string.h>
#include "malloc.h"
+#include "core.h"
+#include <syslinux/memscan.h>
-struct free_arena_header __malloc_head[NHEAP];
+struct free_arena_header __core_malloc_head[NHEAP];
-static __hugebss char main_heap[128 << 10];
+//static __hugebss char main_heap[128 << 10];
extern char __lowmem_heap[];
+extern char free_high_memory[];
+
+#define E820_MEM_MAX 0xfff00000 /* 4 GB - 1 MB */
+int scan_highmem_area(void *data, addr_t start, addr_t len, bool is_ram)
+{
+ struct free_arena_header *fp;
+
+ (void)data;
+
+ dprintf("start = %x, len = %x, is_ram = %d", start, len, is_ram);
+
+ if (start < 0x100000 || start > E820_MEM_MAX
+ || !is_ram)
+ return 0;
+
+ if (start < __com32.cs_memsize) {
+ len -= __com32.cs_memsize - start;
+ start = __com32.cs_memsize;
+ }
+ if (len > E820_MEM_MAX - start)
+ len = E820_MEM_MAX - start;
+
+ if (len >= 2 * sizeof(struct arena_header)) {
+ fp = (struct free_arena_header *)start;
+ fp->a.attrs = ARENA_TYPE_USED | (HEAP_MAIN << ARENA_HEAP_POS);
+#ifdef DEBUG_MALLOC
+ fp->a.magic = ARENA_MAGIC;
+#endif
+ ARENA_SIZE_SET(fp->a.attrs, len);
+ dprintf("will inject a block start:0x%x size 0x%x", start, len);
+ __inject_free_block(fp);
+ }
+
+ __com32.cs_memsize = start + len; /* update the HighMemSize */
+ return 0;
+}
+
+#if 0
+static void mpool_dump(enum heap heap)
+{
+ struct free_arena_header *head = &__core_malloc_head[heap];
+ struct free_arena_header *fp;
+ int size, type, i = 0;
+ addr_t start, end;
+
+ fp = head->next_free;
+ while (fp != head) {
+ size = ARENA_SIZE_GET(fp->a.attrs);
+ type = ARENA_TYPE_GET(fp->a.attrs);
+ start = (addr_t)fp;
+ end = start + size;
+ printf("area[%d]: start = 0x%08x, end = 0x%08x, type = %d\n",
+ i++, start, end, type);
+ fp = fp->next_free;
+ }
+}
+#endif
void mem_init(void)
{
- struct free_arena_header *fp;
- int i;
- uint16_t *bios_free_mem = (uint16_t *)0x413;
+ struct free_arena_header *fp;
+ int i;
+ uint16_t *bios_free_mem = (uint16_t *)0x413;
- /* Initialize the head nodes */
+ //dprintf("enter");
- fp = &__malloc_head[0];
- for (i = 0 ; i < NHEAP ; i++) {
+ /* Initialize the head nodes */
+ fp = &__core_malloc_head[0];
+ for (i = 0 ; i < NHEAP ; i++) {
fp->a.next = fp->a.prev = fp->next_free = fp->prev_free = fp;
fp->a.attrs = ARENA_TYPE_HEAD | (i << ARENA_HEAP_POS);
fp->a.tag = MALLOC_HEAD;
fp++;
- }
-
- /* Initialize the main heap */
- fp = (struct free_arena_header *)main_heap;
- fp->a.attrs = ARENA_TYPE_USED | (HEAP_MAIN << ARENA_HEAP_POS);
- ARENA_SIZE_SET(fp->a.attrs, sizeof main_heap);
- __inject_free_block(fp);
-
- /* Initialize the lowmem heap */
- fp = (struct free_arena_header *)__lowmem_heap;
- fp->a.attrs = ARENA_TYPE_USED | (HEAP_LOWMEM << ARENA_HEAP_POS);
- ARENA_SIZE_SET(fp->a.attrs, (*bios_free_mem << 10) - (uintptr_t)fp);
- __inject_free_block(fp);
+ }
+
+ //dprintf("__lowmem_heap = 0x%p bios_free = 0x%p",
+ // __lowmem_heap, *bios_free_mem);
+
+ /* Initialize the lowmem heap */
+ fp = (struct free_arena_header *)__lowmem_heap;
+ fp->a.attrs = ARENA_TYPE_USED | (HEAP_LOWMEM << ARENA_HEAP_POS);
+ ARENA_SIZE_SET(fp->a.attrs, (*bios_free_mem << 10) - (uintptr_t)fp);
+#ifdef DEBUG_MALLOC
+ fp->a.magic = ARENA_MAGIC;
+#endif
+ __inject_free_block(fp);
+
+ /* Initialize the main heap */
+ __com32.cs_memsize = (size_t)free_high_memory;
+ syslinux_scan_memory(scan_highmem_area, NULL);
}
diff --git a/core/mem/malloc.c b/core/mem/malloc.c
index 78f7b41a..f64850d1 100644
--- a/core/mem/malloc.c
+++ b/core/mem/malloc.c
@@ -4,11 +4,17 @@
* Very simple linked-list based malloc()/free().
*/
+#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <dprintf.h>
+#include <minmax.h>
+
#include "malloc.h"
+#include "thread.h"
+
+DECLARE_INIT_SEMAPHORE(__malloc_semaphore, 1);
static void *__malloc_from_block(struct free_arena_header *fp,
size_t size, malloc_tag_t tag)
@@ -29,6 +35,9 @@ static void *__malloc_from_block(struct free_arena_header *fp,
ARENA_HEAP_SET(nfp->a.attrs, heap);
ARENA_SIZE_SET(nfp->a.attrs, fsize-size);
nfp->a.tag = MALLOC_FREE;
+#ifdef DEBUG_MALLOC
+ nfp->a.magic = ARENA_MAGIC;
+#endif
ARENA_TYPE_SET(fp->a.attrs, ARENA_TYPE_USED);
ARENA_SIZE_SET(fp->a.attrs, size);
fp->a.tag = tag;
@@ -60,11 +69,13 @@ static void *__malloc_from_block(struct free_arena_header *fp,
static void *_malloc(size_t size, enum heap heap, malloc_tag_t tag)
{
struct free_arena_header *fp;
- struct free_arena_header *head = &__malloc_head[heap];
+ struct free_arena_header *head = &__core_malloc_head[heap];
void *p = NULL;
dprintf("_malloc(%zu, %u, %u) @ %p = ",
- size, heap, tag, __builtin_return_address(0));
+ size, heap, tag, __builtin_return_address(0));
+
+ sem_down(&__malloc_semaphore, 0);
if (size) {
/* Add the obligatory arena header, and round up */
@@ -79,21 +90,151 @@ static void *_malloc(size_t size, enum heap heap, malloc_tag_t tag)
}
}
+ sem_up(&__malloc_semaphore);
+
dprintf("%p\n", p);
return p;
}
-void *malloc(size_t size)
+__export void *malloc(size_t size)
{
return _malloc(size, HEAP_MAIN, MALLOC_CORE);
}
-void *lmalloc(size_t size)
+__export void *lmalloc(size_t size)
{
- return _malloc(size, HEAP_LOWMEM, MALLOC_CORE);
+ void *p;
+
+ p = _malloc(size, HEAP_LOWMEM, MALLOC_CORE);
+ if (!p)
+ errno = ENOMEM;
+ return p;
}
void *pmapi_lmalloc(size_t size)
{
return _malloc(size, HEAP_LOWMEM, MALLOC_MODULE);
}
+
+__export void *realloc(void *ptr, size_t size)
+{
+ struct free_arena_header *ah, *nah;
+ struct free_arena_header *head;
+
+ void *newptr;
+ size_t newsize, oldsize, xsize;
+
+ if (!ptr)
+ return malloc(size);
+
+ if (size == 0) {
+ free(ptr);
+ return NULL;
+ }
+
+ ah = (struct free_arena_header *)
+ ((struct arena_header *)ptr - 1);
+
+ head = &__core_malloc_head[ARENA_HEAP_GET(ah->a.attrs)];
+
+#ifdef DEBUG_MALLOC
+ if (ah->a.magic != ARENA_MAGIC)
+ dprintf("failed realloc() magic check: %p\n", ptr);
+#endif
+
+ /* Actual size of the old block */
+ //oldsize = ah->a.size;
+ oldsize = ARENA_SIZE_GET(ah->a.attrs);
+
+ /* Add the obligatory arena header, and round up */
+ newsize = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
+
+ if (oldsize >= newsize && newsize >= (oldsize >> 2) &&
+ oldsize - newsize < 4096) {
+ /* This allocation is close enough already. */
+ return ptr;
+ } else {
+ xsize = oldsize;
+
+ nah = ah->a.next;
+ if ((char *)nah == (char *)ah + ARENA_SIZE_GET(ah->a.attrs) &&
+ ARENA_TYPE_GET(nah->a.attrs) == ARENA_TYPE_FREE &&
+ ARENA_SIZE_GET(nah->a.attrs) + oldsize >= newsize) {
+ //nah->a.type == ARENA_TYPE_FREE &&
+ //oldsize + nah->a.size >= newsize) {
+ /* Merge in subsequent free block */
+ ah->a.next = nah->a.next;
+ ah->a.next->a.prev = ah;
+ nah->next_free->prev_free = nah->prev_free;
+ nah->prev_free->next_free = nah->next_free;
+ ARENA_SIZE_SET(ah->a.attrs, ARENA_SIZE_GET(ah->a.attrs) +
+ ARENA_SIZE_GET(nah->a.attrs));
+ xsize = ARENA_SIZE_GET(ah->a.attrs);
+ }
+
+ if (xsize >= newsize) {
+ /* We can reallocate in place */
+ if (xsize >= newsize + 2 * sizeof(struct arena_header)) {
+ /* Residual free block at end */
+ nah = (struct free_arena_header *)((char *)ah + newsize);
+ ARENA_TYPE_SET(nah->a.attrs, ARENA_TYPE_FREE);
+ ARENA_SIZE_SET(nah->a.attrs, xsize - newsize);
+ ARENA_SIZE_SET(ah->a.attrs, newsize);
+ ARENA_HEAP_SET(nah->a.attrs, ARENA_HEAP_GET(ah->a.attrs));
+
+#ifdef DEBUG_MALLOC
+ nah->a.magic = ARENA_MAGIC;
+#endif
+
+ //nah->a.type = ARENA_TYPE_FREE;
+ //nah->a.size = xsize - newsize;
+ //ah->a.size = newsize;
+
+ /* Insert into block list */
+ nah->a.next = ah->a.next;
+ ah->a.next = nah;
+ nah->a.next->a.prev = nah;
+ nah->a.prev = ah;
+
+ /* Insert into free list */
+ if (newsize > oldsize) {
+ /* Hack: this free block is in the path of a memory object
+ which has already been grown at least once. As such, put
+ it at the *end* of the freelist instead of the beginning;
+ trying to save it for future realloc()s of the same block. */
+ nah->prev_free = head->prev_free;
+ nah->next_free = head;
+ head->prev_free = nah;
+ nah->prev_free->next_free = nah;
+ } else {
+ nah->next_free = head->next_free;
+ nah->prev_free = head;
+ head->next_free = nah;
+ nah->next_free->prev_free = nah;
+ }
+ }
+ /* otherwise, use up the whole block */
+ return ptr;
+ } else {
+ /* Last resort: need to allocate a new block and copy */
+ oldsize -= sizeof(struct arena_header);
+ newptr = malloc(size);
+ if (newptr) {
+ memcpy(newptr, ptr, min(size, oldsize));
+ free(ptr);
+ }
+ return newptr;
+ }
+ }
+}
+
+__export void *zalloc(size_t size)
+{
+ void *ptr;
+
+ ptr = malloc(size);
+ if (ptr)
+ memset(ptr, 0, size);
+
+ return ptr;
+}
diff --git a/core/mem/malloc.h b/core/mem/malloc.h
index b8ec44d7..4611a306 100644
--- a/core/mem/malloc.h
+++ b/core/mem/malloc.h
@@ -7,6 +7,9 @@
#include <stdint.h>
#include <stddef.h>
#include "core.h"
+#include "thread.h"
+
+extern struct semaphore __malloc_semaphore;
/*
* This is a temporary hack. In Syslinux 5 this will be a pointer to
@@ -20,6 +23,20 @@ enum malloc_owner {
MALLOC_MODULE,
};
+enum arena_type {
+ ARENA_TYPE_USED = 0,
+ ARENA_TYPE_FREE = 1,
+ ARENA_TYPE_HEAD = 2,
+ ARENA_TYPE_DEAD = 3,
+};
+enum heap {
+ HEAP_MAIN,
+ HEAP_LOWMEM,
+ NHEAP
+};
+
+#define ARENA_MAGIC 0x20130117
+
struct free_arena_header;
/*
@@ -32,18 +49,27 @@ struct arena_header {
2..3: Heap,
4..31: MSB of the size */
struct free_arena_header *next, *prev;
-};
-enum arena_type {
- ARENA_TYPE_USED = 0,
- ARENA_TYPE_FREE = 1,
- ARENA_TYPE_HEAD = 2,
- ARENA_TYPE_DEAD = 3,
+#ifdef DEBUG_MALLOC
+ unsigned long _pad[3];
+ unsigned int magic;
+#endif
};
-enum heap {
- HEAP_MAIN,
- HEAP_LOWMEM,
- NHEAP
+
+/* Pad to 2*sizeof(struct arena_header) */
+#define ARENA_PADDING ((2 * sizeof(struct arena_header)) - \
+ (sizeof(struct arena_header) + \
+ sizeof(struct free_arena_header *) + \
+ sizeof(struct free_arena_header *)))
+
+/*
+ * This structure should be no more than twice the size of the
+ * previous structure.
+ */
+struct free_arena_header {
+ struct arena_header a;
+ struct free_arena_header *next_free, *prev_free;
+ size_t _pad[ARENA_PADDING];
};
#define ARENA_SIZE_MASK (~(uintptr_t)(sizeof(struct arena_header)-1))
@@ -68,15 +94,5 @@ enum heap {
((attrs) = ((attrs) & ~ARENA_TYPE_MASK) | \
((type) & ARENA_TYPE_MASK))
-/*
- * This structure should be no more than twice the size of the
- * previous structure.
- */
-struct free_arena_header {
- struct arena_header a;
- struct free_arena_header *next_free, *prev_free;
- size_t _pad[2]; /* Pad to 2*sizeof(struct arena_header) */
-};
-
-extern struct free_arena_header __malloc_head[NHEAP];
+extern struct free_arena_header __core_malloc_head[NHEAP];
void __inject_free_block(struct free_arena_header *ah);
diff --git a/core/parsecmd.inc b/core/parsecmd.inc
deleted file mode 100644
index 30cd67fe..00000000
--- a/core/parsecmd.inc
+++ /dev/null
@@ -1,129 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; parsecmd.inc
-;;
-;; Command line parser code
-;;
-
- section .text16
-
-; -------------------------------------------------------------------------
-; getcommand: Get a keyword from the current "getc" file and match it
-; against a list of keywords (keywd_table). Each entry in
-; that table should have the following form:
-; <32 bit hash value> <16 bit handler offset>
-;
-; The handler is called, and upon return this function
-; returns with CF = 0. On EOF, this function returns
-; with CF = 1.
-; -------------------------------------------------------------------------
-
-getcommand.skipline:
- call skipline
-
-getcommand:
-.find:
- call skipspace ; Skip leading whitespace
- jz .eof ; End of file
- jc .find ; End of line: try again
-
- ; Do this explicitly so #foo is treated as a comment
- cmp al,'#' ; Leading hash mark -> comment
- je .skipline
-
- ; Abuse the trackbuf by putting the keyword there for
- ; possible error messaging...
- mov di,trackbuf
- stosb
- or al,20h ; Convert to lower case
- movzx ebx,al ; Hash for a one-char keyword
-.read_loop:
- call getc
- jc .eof
- cmp al,' ' ; Whitespace
- jbe .done
- stosb
- or al,20h
- rol ebx,5
- xor bl,al
- jmp short .read_loop
-.done: call ungetc
- xor ax,ax
- stosb ; Null-terminate the trackbuf
- call skipspace
- jz .eof
- jc .noparm
- call ungetc ; Return nonwhitespace char to buf
- mov si,keywd_table
- mov cx,keywd_count
-.table_search:
- lodsd
- cmp ebx,eax
- je .found_keywd
- lodsd ; Skip entrypoint/argument
- loop .table_search
-
- ; Otherwise unrecognized keyword
- mov si,err_badcfg
- call writestr
- mov si,trackbuf
- call writestr
- call crlf
- jmp .skipline
-
- ; No parameter
-.noparm:
- mov si,err_noparm
- call writestr
- mov si,trackbuf
- call writestr
- call crlf
- jmp .find
-
-.found_keywd: lodsw ; Load argument into ax
- call [si]
- clc
- ret
-
-.eof: stc
- ret
-
-skipline: cmp al,10 ; Search for LF
- je .end
- call getc
- jnc skipline
-.end: ret
-
- section .data16
-err_badcfg db 'Unknown keyword in configuration file: ',0
-err_noparm db 'Missing parameter in configuration file. Keyword: ',0
-
- section .uibss
- alignb 4
-vk_size equ (vk_end + 3) & ~3
-VKernelBuf: resb vk_size ; "Current" vkernel
-AppendBuf resb max_cmd_len+1 ; append=
-Ontimeout resb max_cmd_len+1 ; ontimeout
-Onerror resb max_cmd_len+1 ; onerror
- ; This could be in .uibss but that makes PXELINUX overflow
- section .bss16
-KbdMap resb 256 ; Keyboard map
-FKeyName resb MAX_FKEYS*FILENAME_MAX ; File names for F-key help
- global KernelName
-KernelName resb FILENAME_MAX ; Mangled name for kernel
-MNameBuf resb FILENAME_MAX
-InitRD resb FILENAME_MAX
-
- section .text16
diff --git a/core/parseconfig.inc b/core/parseconfig.inc
deleted file mode 100644
index 512f16b4..00000000
--- a/core/parseconfig.inc
+++ /dev/null
@@ -1,473 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; parseconfig.inc
-;;
-;; Configuration file operations
-;;
-
- section .text16
-;
-; "default" or "ui" command, with level (1 = default, 2 = ui)
-;
-pc_default: cmp ax,[DefaultLevel]
- jb skipline ; == call skipline + ret
- mov [DefaultLevel],ax
- mov di,default_cmd
- call getline
- mov byte [di-1],0 ; null-terminate
- ret
-
-;
-; "ontimeout" command
-;
-pc_ontimeout: mov di,Ontimeout
- call getline
- sub di,Ontimeout+1 ; Don't need final space
- mov [OntimeoutLen],di
- ret
-
-;
-; "onerror" command
-;
-pc_onerror: mov di,Onerror
- call getline
- sub di,Onerror
- mov [OnerrorLen],di
- ret
-
-;
-; "append" command
-;
-pc_append: cmp byte [VKernel],0
- ja .vk
- mov di,AppendBuf
- call getline
- sub di,AppendBuf
-.app1: mov [AppendLen],di
- ret
-.vk: mov di,VKernelBuf+vk_append ; "append" command (vkernel)
- call getline
- sub di,VKernelBuf+vk_append
- cmp di,byte 2
- jne .app2
- cmp byte [VKernelBuf+vk_append],'-'
- jne .app2
- xor di,di ; If "append -" -> null string
-.app2: mov [VKernelBuf+vk_appendlen],di
- ret
-
-;
-; "ipappend" command (PXELINUX only)
-;
-%if IS_PXELINUX
-pc_ipappend: call getint
- jc .err
- cmp byte [VKernel],0
- jne .vk
- mov [IPAppend],bl
-.err: ret
-.vk: mov [VKernelBuf+vk_ipappend],bl
- ret
-%endif
-
-;
-; "localboot" command
-;
-pc_localboot: call getint
- cmp byte [VKernel],0 ; ("label" section only)
- je .err
- mov [VKernelBuf+vk_rname],bx
- mov byte [VKernelBuf+vk_type],VK_LOCALBOOT
-.err: ret
-
-;
-; "kernel", "config", ... command
-;
-pc_kernel: cmp byte [VKernel],0
- je .err ; ("label" section only)
- mov [VKernelBuf+vk_type],al
- call pc_getline
- mov di,VKernelBuf+vk_rname
- pm_call pm_mangle_name
-.err: ret
-
-;
-; "timeout", "totaltimeout" command
-;
-; N.B. 1/10 s ~ 1.D2162AABh clock ticks
-;
-pc_timeout: push ax
- call getint
- pop si
- jc .err
- mov eax,0D2162AABh
- mul ebx ; clock ticks per 1/10 s
- add ebx,edx
- mov [si],ebx
-.err: ret
-
-
-;
-; "totaltimeout" command
-;
-pc_totaltimeout:
-
-;
-; Generic integer variable setting commands:
-; "prompt", "implicit"
-;
-pc_setint16:
- push ax
- call getint
- pop si
- jc .err
- mov [si],bx
-.err: ret
-
-;
-; Generic file-processing commands:
-; "font", "kbdmap",
-;
-pc_filecmd: push ax ; Function to tailcall
- call pc_getline
- mov di,MNameBuf
- pm_call pm_mangle_name
- pm_call pm_searchdir
- jnz .ok
- pop ax ; Drop the successor function
-.ok: ret ; Tailcall if OK, error return
-
-;
-; Commands that expect the file to be opened on top of the getc stack.
-; "display", "include"
-;
-pc_opencmd: push ax ; Function to tailcall
- call pc_getline
- mov di,MNameBuf
- pm_call pm_mangle_name
- call core_open
- jnz .ok
- pop ax ; Drop the successor function
-.ok: ret ; Tailcall if OK, error return
-
-;
-; "include" command (invoked from pc_opencmd)
-;
-pc_include: inc word [IncludeLevel]
-.err: ret
-
-;
-; "serial" command
-;
-pc_serial: call getint
- jc .err
- push bx ; Serial port #
- xor eax,eax
- mov [FlowControl],eax ; Default to no flow control
- call skipspace
- jc .nobaud
- call ungetc
- call getint
- jc .nobaud
-.valid_baud:
- push ebx
- call skipspace
- jc .no_flow
- call ungetc
- call getint ; Hardware flow control?
- jnc .valid_flow
-.no_flow:
- xor bx,bx ; Default -> no flow control
-.valid_flow:
- and bh,0Fh ; FlowIgnore
- shl bh,4
- mov [FlowIgnore],bh
- mov bh,bl
- and bx,0F00Bh ; Valid bits
- mov [FlowControl],bx
- pop ebx ; Baud rate
- jmp short .parse_baud
-.nobaud:
- mov ebx,DEFAULT_BAUD ; No baud rate given
-.parse_baud:
- pop di ; Serial port #
- cmp ebx,byte 75
- jb .err ; < 75 baud == bogus
- mov eax,BAUD_DIVISOR
- cdq
- div ebx
- mov [BaudDivisor],ax
- push ax ; Baud rate divisor
- cmp di,3
- ja .port_is_io ; If port > 3 then port is I/O addr
- shl di,1
- mov di,[di+serial_base] ; Get the I/O port from the BIOS
-.port_is_io:
- mov [SerialPort],di
-
- ;
- ; Begin code to actually set up the serial port
- ;
- call sirq_cleanup_nowipe ; Cleanup existing IRQ handler
-
- lea dx,[di+3] ; DX -> LCR
- mov al,83h ; Enable DLAB
- slow_out dx,al
-
- pop ax ; Divisor
- mov dx,di ; DX -> LS
- slow_out dx,al
-
- inc dx ; DX -> MS
- mov al,ah
- slow_out dx,al
-
- mov al,03h ; Disable DLAB
- inc dx ; DX -> LCR
- inc dx
- slow_out dx,al
-
- in al,dx ; Read back LCR (detect missing hw)
- cmp al,03h ; If nothing here we'll read 00 or FF
- jne .err ; Assume serial port busted
- dec dx ; DX -> IIR/FCR
- mov al,01h
- slow_out dx,al ; Enable FIFOs if present
- in al,dx
- cmp al,0C0h ; FIFOs enabled and usable?
- jae .fifo_ok
- xor ax,ax ; Disable FIFO if unusable
- slow_out dx,al
-.fifo_ok:
-
- inc dx
- inc dx ; DX -> MCR
- mov al,[FlowOutput] ; Assert bits
- slow_out dx,al
-
- ; Enable interrupts if requested
- test al,8
- jz .noirq
- call sirq_install
-.noirq:
-
- ; Show some life
- cmp byte [SerialNotice],0
- je .notfirst
- mov byte [SerialNotice],0
-
- mov si,syslinux_banner
- call write_serial_str
- mov si,copyright_str
- call write_serial_str
-.notfirst:
- ret
-
-.err:
- mov [SerialPort], word 0
- ret
-
-;
-; Store mangled filename command (F-keys, "initrd")
-;
-pc_filename: push ax
- call pc_getline
- pop di
- pm_call pm_mangle_name ; Mangle file name
- ret
-
-;
-; "label" command
-;
-pc_label: call commit_vk ; Commit any current vkernel
- mov byte [InitRD+NULLOFFSET],NULLFILE ; No "initrd" statement
- mov di,VKernelBuf ; Erase the vkernelbuf for better compression
- mov cx,(vk_size >> 1)
- xor ax,ax
- rep stosw
- call pc_getline
- mov di,VKernelBuf+vk_vname
- mov cx,FILENAME_MAX-1
-.loop:
- lodsb
- cmp al,' '
- jna .done
- stosb
- loop .loop
-.done:
- mov byte [VKernel],1 ; We've seen a "label" statement
- mov si,VKernelBuf+vk_vname ; By default, rname == mangled vname
- mov di,VKernelBuf+vk_rname
- pm_call pm_mangle_name
- mov si,AppendBuf ; Default append==global append
- mov di,VKernelBuf+vk_append
- mov cx,[AppendLen]
- mov [VKernelBuf+vk_appendlen],cx
- rep movsb
-%if IS_PXELINUX ; PXELINUX only
- mov al,[IPAppend] ; Default ipappend==global ipappend
- mov [VKernelBuf+vk_ipappend],al
-%endif
- ret
-
-;
-; "say" command
-;
-pc_say: call pc_getline ; "say" command
- call writestr
- jmp crlf ; tailcall
-
-;
-; "text" command; ignore everything until we get an "endtext" line
-;
-pc_text: call skipline ; Ignore rest of line
-.loop:
- call pc_getline
- jc .eof
-
- ; Leading spaces are already removed...
- lodsd
- and eax,0xdfdfdfdf ; Upper case
- cmp eax,'ENDT'
- jne .loop
- lodsd
- and eax,0x00dfdfdf ; Upper case and mask
- cmp eax,'EXT'
- jne .loop
- ; If we get here we hit ENDTEXT
-.eof:
- ret
-
-;
-; Comment line
-;
-pc_comment: ; Fall into pc_getline
-
-;
-; Common subroutine: load line into trackbuf; returns with SI -> trackbuf
-; CF is set on EOF.
-;
-pc_getline: mov di,trackbuf
- push di
- call getline
- mov byte [di],0 ; Null-terminate
- pop si
- ret
-
-;
-; Main loop for configuration file parsing
-;
-parse_config:
- mov di,VKernelBuf ; Clear VKernelBuf at start
- xor ax,ax
- mov cx,vk_size
- rep stosb
-
-.again:
- call getcommand ; Parse one command
- jnc .again ; If not EOF...
- call close
- dec word [IncludeLevel] ; Still parsing?
- jnz .again
-
- ;
- ; The fall through to commit_vk to commit any final
- ; VKernel being read
- ;
-;
-; commit_vk: Store the current VKernelBuf into buffer segment
-;
-commit_vk:
- cmp byte [VKernel],0
- jz .nolabel ; Nothing to commit...
-
- mov di,VKernelBuf+vk_append
- add di,[VKernelBuf+vk_appendlen]
-
- ; If we have an initrd statement, append it to the
- ; append statement
- cmp byte [InitRD+NULLOFFSET],NULLFILE
- je .noinitrd
-
- mov si,str_initrd
- mov cx,7 ; "initrd="
- rep movsb
- mov si,InitRD
- call strcpy
- mov byte [es:di-1],' '
-
- ; For better compression, clean up the append field
-.noinitrd:
- mov ax,di
- sub ax,VKernelBuf+vk_append
- mov [VKernelBuf+vk_appendlen],ax
- mov cx,max_cmd_len+1
- sub cx,ax
- xor ax,ax
- rep stosb
-
- ; Pack into high memory
- mov esi,VKernelBuf
- mov edi,[VKernelEnd]
- mov ecx,vk_size
- pm_call rllpack
- mov [VKernelEnd],edi
-.nolabel:
- ret
-.overflow:
- mov si,vk_overflow_msg
- call writestr
- ret
-
- section .data16
-vk_overflow_msg db 'Out of memory parsing config file', CR, LF, 0
-SerialNotice db 1 ; Only print this once
-
- section .bss16
- alignb 4
-VKernelEnd resd 1 ; Lowest high memory address used
-
- ; This symbol should be used by loaders to indicate
- ; the highest address *they* are allowed to use.
-HighMemRsvd equ VKernelEnd
- ; by vkernels
- section .config
- alignz 4
-KbdTimeout dd 0 ; Keyboard timeout (if any)
-TotalTimeout dd 0 ; Total timeout (if any)
-AppendLen dw 0 ; Bytes in append= command
-OntimeoutLen dw 0 ; Bytes in ontimeout command
-OnerrorLen dw 0 ; Bytes in onerror command
-CmdLinePtr dw cmd_line_here ; Command line advancing pointer
-ForcePrompt dw 0 ; Force prompt
-NoEscape dw 0 ; No escape
-NoComplete dw 0 ; No label completion on TAB key
-AllowImplicit dw 1 ; Allow implicit kernels
-AllowOptions dw 1 ; User-specified options allowed
-IncludeLevel dw 1 ; Nesting level
-DefaultLevel dw 0 ; The current level of default
- global PXERetry
-PXERetry dw 0 ; Extra PXE retries
-VKernel db 0 ; Have we seen any "label" statements?
-
-%if IS_PXELINUX
-IPAppend db 0 ; Default IPAPPEND option
-%endif
-
- section .uibss
- alignb 4 ; For the good of REP MOVSD
-command_line resb max_cmd_len+2 ; Command line buffer
- alignb 4
-default_cmd resb max_cmd_len+1 ; "default" command line
diff --git a/core/path.c b/core/path.c
new file mode 100644
index 00000000..8e517ca7
--- /dev/null
+++ b/core/path.c
@@ -0,0 +1,42 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2013 Intel Corporation; author: Matt Fleming
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <klibc/compiler.h>
+#include <linux/list.h>
+#include <fs.h>
+#include <string.h>
+
+__export LIST_HEAD(PATH);
+
+__export struct path_entry *path_add(const char *str)
+{
+ struct path_entry *entry;
+
+ if (!strlen(str))
+ return NULL;
+
+ entry = malloc(sizeof(*entry));
+ if (!entry)
+ return NULL;
+
+ entry->str = strdup(str);
+ if (!entry->str)
+ goto bail;
+
+ list_add(&entry->list, &PATH);
+
+ return entry;
+
+bail:
+ free(entry);
+ return NULL;
+}
diff --git a/core/plaincon.c b/core/plaincon.c
new file mode 100644
index 00000000..8f8ca7ca
--- /dev/null
+++ b/core/plaincon.c
@@ -0,0 +1,34 @@
+#include <sys/io.h>
+#include <fs.h>
+#include <com32.h>
+
+#include "bios.h"
+#include "graphics.h"
+
+/*
+ * Write a single character in AL to the console without
+ * mangling any registers; handle video pages correctly.
+ */
+__export void writechr(char data)
+{
+ com32sys_t ireg, oreg;
+
+ write_serial(data); /* write to serial port if needed */
+
+ if (UsingVGA & 0x8)
+ syslinux_force_text_mode();
+
+ if (!(DisplayCon & 0x1))
+ return;
+
+ ireg.eax.b[0] = data;
+ ireg.eax.b[1] = 0xE;
+ ireg.ebx.b[0] = 0x07; /* attribute */
+ ireg.ebx.b[1] = *(uint8_t *)BIOS_page; /* current page */
+ __intcall(0x10, &ireg, &oreg);
+}
+
+void pm_writechr(com32sys_t *regs)
+{
+ writechr(regs->eax.b[0]);
+}
diff --git a/core/plaincon.inc b/core/plaincon.inc
deleted file mode 100644
index c41629d0..00000000
--- a/core/plaincon.inc
+++ /dev/null
@@ -1,24 +0,0 @@
-;
-; writechr: Write a single character in AL to the console without
-; mangling any registers; handle video pages correctly.
-;
- section .text16
-
-writechr:
- call write_serial ; write to serial port if needed
- pushfd
- test byte [cs:UsingVGA], 08h
- jz .videook
- call vgaclearmode
-.videook:
- test byte [cs:DisplayCon], 01h
- jz .nothing
- pushad
- mov ah,0Eh
- mov bl,07h ; attribute
- mov bh,[cs:BIOS_page] ; current page
- int 10h
- popad
-.nothing:
- popfd
- ret
diff --git a/core/pm.inc b/core/pm.inc
index 9584cda1..7b98944f 100644
--- a/core/pm.inc
+++ b/core/pm.inc
@@ -194,6 +194,7 @@ PM_IDT_ptr: dw 8*256-1 ; Length
pm_irq:
pushad
movzx esi,byte [esp+8*4] ; Interrupt number
+ inc dword [CallbackCtr]
mov ebx,.rm
jmp enter_rm ; Go to real mode
@@ -208,10 +209,28 @@ pm_irq:
bits 32
section .textnr
.pm:
+ dec dword [CallbackCtr]
+ jnz .skip
+ call [core_pm_hook]
+.skip:
popad
add esp,4 ; Drop interrupt number
iretd
+;
+; Initially, the core_pm_hook does nothing; it is available for the
+; threaded derivatives to run the scheduler, or examine the result from
+; interrupt routines.
+;
+ global core_pm_null_hook
+core_pm_null_hook:
+ ret
+
+ section .data16
+ alignz 4
+ global core_pm_hook
+core_pm_hook: dd core_pm_null_hook
+
bits 16
section .text16
;
@@ -328,7 +347,8 @@ a20_fast:
jnz a20_dunno ; Did we get the wrong type?
mov si, err_a20
- jmp abort_load
+ pm_call pm_writestr
+ jmp kaboom
section .data16
err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
@@ -440,6 +460,7 @@ pm_init:
section .earlybss
alignb 8
IDT: resq 256
+ global RealModeSSSP
RealModeSSSP resd 1 ; Real-mode SS:SP
section .gentextnr ; Autogenerated 32-bit code
diff --git a/core/pmapi.c b/core/pmapi.c
index 4b1ccbb1..cfdffa69 100644
--- a/core/pmapi.c
+++ b/core/pmapi.c
@@ -36,8 +36,11 @@ const struct com32_pmapi pm_api_vector =
.reset_idle = reset_idle,
.chdir = chdir,
- .getcwd = getcwd,
+ .getcwd = core_getcwd,
.jiffies = &__jiffies,
.ms_timer = &__ms_timer,
+
+ .sysappend_count = SYSAPPEND_MAX,
+ .sysappend_strings = sysappend_strings,
};
diff --git a/core/printf.c b/core/printf.c
deleted file mode 100644
index b1b0466b..00000000
--- a/core/printf.c
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <stdio.h>
-#include <unistd.h>
-
-#include "core.h"
-
-int printf(const char *format, ...)
-{
- char buf[1024];
- va_list ap;
- int rv;
-
- va_start(ap, format);
- rv = vsnprintf(buf, sizeof buf, format, ap);
- va_end(ap);
-
- myputs(buf);
-
- return rv;
-
-}
diff --git a/core/pxe.inc b/core/pxe.inc
index 2fd1edb7..9b46bb67 100644
--- a/core/pxe.inc
+++ b/core/pxe.inc
@@ -152,4 +152,12 @@
%define PXENV_STATUS_LOADER_UNDI_START 0xca
%define PXENV_STATUS_LOADER_BC_START 0xcb
+; UNDI ISR codes
+%define PXENV_UNDI_ISR_IN_START 1
+%define PXENV_UNDI_ISR_IN_PROCESS 2
+%define PXENV_UNDI_ISR_IN_GET_NEXT 3
+
+%define PXENV_UNDI_ISR_OUT_OURS 0
+%define PXENV_UNDI_ISR_OUT_NOT_OURS 1
+
%endif ; _PXE_INC
diff --git a/core/pxeboot.c b/core/pxeboot.c
new file mode 100644
index 00000000..b6c90998
--- /dev/null
+++ b/core/pxeboot.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ */
+
+#include <syslinux/video.h>
+#include "pxe.h"
+#include <com32.h>
+
+#define LOCALBOOT_MSG "Booting from local disk..."
+
+extern void local_boot16(void);
+
+/*
+ * Boot to the local disk by returning the appropriate PXE magic.
+ * AX contains the appropriate return code.
+ */
+__export void local_boot(uint16_t ax)
+{
+ com32sys_t ireg;
+
+ syslinux_force_text_mode();
+
+ writestr(LOCALBOOT_MSG);
+ crlf();
+
+ /* Restore the environment we were called with */
+ reset_pxe();
+
+ cleanup_hardware();
+
+ ireg.eax.w[0] = ax;
+ call16(local_boot16, &ireg, NULL);
+}
diff --git a/core/pxeisr.inc b/core/pxeisr.inc
new file mode 100644
index 00000000..93c73ed5
--- /dev/null
+++ b/core/pxeisr.inc
@@ -0,0 +1,172 @@
+;
+; Process a PXE interrupt
+;
+ section .text16
+
+PXEIRQ_MAX equ 100 ; Max spurious interrupts in a timer tick
+
+ global pxe_isr
+pxe_isr:
+ cld
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ xor ax,ax
+ mov ds,ax
+ mov es,ax
+
+ mov bx,PXENV_UNDI_ISR
+ mov di,pxenv_undi_isr_buf
+
+ mov cx,pxenv_undi_isr_buf.size/2
+ push di
+ rep stosw
+ pop di
+
+ mov byte [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_IN_START
+
+ call pxenv
+ mov ax,[__jiffies]
+ jc .notus
+
+ cmp word [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_OUT_OURS
+ jne .notus
+
+ ; Its ours - set the flag for the return to PM.
+ ; We need to EOI this ourselves, so that the
+ ; leftover BC doesn't get control.
+ mov byte [pxe_irq_pending],1
+ inc dword [pxe_irq_count]
+
+ cmp byte [pxe_irq_vector], 8
+ mov al,0x20 ; Non-specific EOI
+ jb .pri_pic
+
+ out 0xA0, al ; Secondary PIC
+.pri_pic:
+ out 0x20,al ; Primary PIC
+
+ mov [pxeirq_last],ax
+ mov word [pxeirq_deadman],PXEIRQ_MAX
+
+.exit:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ iret
+
+.notus:
+ cmp ax,[pxeirq_last]
+ jne .reset_timeout
+ dec word [pxeirq_deadman]
+ jz .timeout
+
+.chain:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ jmp 0:0
+ global pxe_irq_chain
+pxe_irq_chain equ $-4
+
+.reset_timeout:
+ mov [pxeirq_last],ax
+ mov word [pxeirq_deadman],PXEIRQ_MAX
+ jmp .chain
+
+ ; Too many spurious interrupts, shut off the interrupts
+ ; and go to polling mode
+.timeout:
+ mov al,[pxe_irq_vector]
+ mov dx,21h
+ movzx cx,al
+ shl cx,7-3
+ add dx,cx
+ and al,7
+ xchg ax,cx
+ mov ch,1
+ shl ch,cl
+ in al,dx
+ or al,ch
+ out dx,al
+ or byte [pxe_need_poll],1
+ jmp .exit
+
+
+; Emulate a PXE interrupt from the polling thread
+ global pxe_poll
+pxe_poll:
+ pushf
+ cli
+ cld
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov bx,PXENV_UNDI_ISR
+ mov di,pxenv_undi_isr_buf
+
+ mov cx,pxenv_undi_isr_buf.size/2
+ push di
+ rep stosw
+ pop di
+
+ mov byte [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_IN_START
+
+ call pxenv
+ jc .notus
+
+ cmp word [pxenv_undi_isr_buf.funcflag],PXENV_UNDI_ISR_OUT_OURS
+ jne .notus
+
+ ; Its ours - set the flag for the return to PM.
+ ; We need to EOI this ourselves, so that the
+ ; leftover BC doesn't get control.
+ mov byte [pxe_irq_pending],1
+
+.notus:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ popf
+ ret
+
+ section .bss16
+ alignb 4
+pxenv_undi_isr_buf:
+.status: resw 1
+.funcflag: resw 1
+.bufferlength: resw 1
+.framelen: resw 1
+.framehdrlen: resw 1
+.frame: resw 2
+.prottype: resb 1
+.pkttype: resb 1
+.size equ $-pxenv_undi_isr_buf
+
+ alignb 4
+pxeirq_last resw 1
+pxeirq_deadman resw 1
+
+ global pxe_irq_count
+pxe_irq_count resd 1 ; PXE IRQ counter
+ global pxe_irq_vector
+pxe_irq_vector resb 1 ; PXE IRQ vector
+ global pxe_irq_pending
+pxe_irq_pending resb 1 ; IRQ pending flag
+ global pxe_need_poll
+pxe_need_poll resb 1 ; Bit 0 = need polling
+ ; Bit 1 = polling active
+
+ section .text16
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
index e8818a6c..2c9b9f86 100644
--- a/core/pxelinux.asm
+++ b/core/pxelinux.asm
@@ -39,25 +39,6 @@ TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
SECTOR_SIZE equ TFTP_BLOCKSIZE
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_ipappend: resb 1 ; "IPAPPEND" flag
-vk_type: resb 1 ; Type of file
-vk_appendlen: resw 1
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -81,14 +62,16 @@ InitStack resd 1
PXEStack resd 1 ; Saved stack during PXE call
alignb 4
- global DHCPMagic, RebootTime, APIVer
+ global DHCPMagic, RebootTime, APIVer, BIOSName
RebootTime resd 1 ; Reboot timeout, if set by option
StrucPtr resw 2 ; Pointer to PXENV+ or !PXE structure
APIVer resw 1 ; PXE API version found
LocalBootType resw 1 ; Local boot return code
DHCPMagic resb 1 ; PXELINUX magic flags
+BIOSName resw 1 ; Dummy variable - always 0
section .text16
+ global StackBuf
StackBuf equ STACK_TOP-44 ; Base of stack if we use our own
StackHome equ StackBuf
@@ -154,54 +137,6 @@ _start1:
lss esp,[BaseStack]
sti ; Stack set up and ready
-;
-; Move the hardwired DHCP options (if present) to a safe place...
-;
-bdhcp_copy:
- mov cx,[bdhcp_len]
- mov ax,trackbufsize/2
- jcxz .none
- cmp cx,ax
- jbe .oksize
- mov cx,ax
- mov [bdhcp_len],ax
-.oksize:
- mov eax,[bdhcp_offset]
- add eax,_start
- mov si,ax
- and si,000Fh
- shr eax,4
- push ds
- mov ds,ax
- mov di,trackbuf
- add cx,3
- shr cx,2
- rep movsd
- pop ds
-.none:
-
-adhcp_copy:
- mov cx,[adhcp_len]
- mov ax,trackbufsize/2
- jcxz .none
- cmp cx,ax
- jbe .oksize
- mov cx,ax
- mov [adhcp_len],ax
-.oksize:
- mov eax,[adhcp_offset]
- add eax,_start
- mov si,ax
- and si,000Fh
- shr eax,4
- push ds
- mov ds,ax
- mov di,trackbuf+trackbufsize/2
- add cx,3
- shr cx,2
- rep movsd
- pop ds
-.none:
;
; Initialize screen (if we're using one)
@@ -236,11 +171,11 @@ ROOT_FS_OPS:
;
; Initialize the idle mechanism
;
- call reset_idle
+ extern reset_idle
+ pm_call reset_idle
;
-; Now we're all set to start with our *real* business. First load the
-; configuration file (if any) and parse it.
+; Now we're all set to start with our *real* business.
;
; In previous versions I avoided using 32-bit registers because of a
; rumour some BIOSes clobbered the upper half of 32-bit registers at
@@ -264,31 +199,102 @@ ROOT_FS_OPS:
%endmacro
;
-; Load configuration file
+; Jump to 32-bit ELF space
;
- pm_call pm_load_config
- jz no_config_file
+ pm_call load_env32
+ jmp kaboom ; load_env32() shouldn't return. If it does, then kaboom!
+
+print_hello:
+enter_command:
+auto_boot:
+ pm_call hello
;
-; Now we have the config file open. Parse the config file and
-; run the user interface.
+; Save hardwired DHCP options. This is done before the C environment
+; is initialized, so it has to be done in assembly.
;
-%include "ui.inc"
+%define MAX_DHCP_OPTS 4096
+ bits 32
+
+ section .savedata
+ global bdhcp_data, adhcp_data
+bdhcp_data: resb MAX_DHCP_OPTS
+adhcp_data: resb MAX_DHCP_OPTS
+
+ section .textnr
+pm_save_data:
+ mov eax,MAX_DHCP_OPTS
+ movzx ecx,word [bdhcp_len]
+ cmp ecx,eax
+ jna .oksize
+ mov ecx,eax
+ mov [bdhcp_len],ax
+.oksize:
+ mov esi,[bdhcp_offset]
+ add esi,_start
+ mov edi,bdhcp_data
+ add ecx,3
+ shr ecx,2
+ rep movsd
+
+adhcp_copy:
+ movzx ecx,word [adhcp_len]
+ cmp ecx,eax
+ jna .oksize
+ mov ecx,eax
+ mov [adhcp_len],ax
+.oksize:
+ mov esi,[adhcp_offset]
+ add esi,_start
+ mov edi,adhcp_data
+ add ecx,3
+ shr ecx,2
+ rep movsd
+ ret
+
+ bits 16
+
+; As core/ui.inc used to be included here in core/pxelinux.asm, and it's no
+; longer used, its global variables that were previously used by
+; core/pxelinux.asm are now declared here.
+ section .bss16
+ alignb 4
+Kernel_EAX resd 1
+Kernel_SI resw 1
+ section .bss16
+ alignb 4
+ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
+ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
+KernelExtPtr resw 1 ; During search, final null pointer
+FuncFlag resb 1 ; Escape sequences received from keyboard
+KernelType resb 1 ; Kernel type, from vkernel, if known
+ global KernelName
+KernelName resb FILENAME_MAX ; Mangled name for kernel
+
+ section .text16
;
-; Boot to the local disk by returning the appropriate PXE magic.
-; AX contains the appropriate return code.
+; COMBOOT-loading code
;
-local_boot:
- push cs
- pop ds
+%include "comboot.inc"
+%include "com32.inc"
+
+;
+; Boot sector loading code
+;
+
+;
+; Abort loading code
+;
+
+;
+; Hardware cleanup common code
+;
+
+ section .text16
+ global local_boot16:function hidden
+local_boot16:
mov [LocalBootType],ax
- call vgaclearmode
- mov si,localboot_msg
- call writestr_early
- ; Restore the environment we were called with
- pm_call reset_pxe
- call cleanup_hardware
lss sp,[InitStack]
pop gs
pop fs
@@ -335,20 +341,19 @@ kaboom:
.wait2: mov dx,[BIOS_timer]
.wait3: call pollchar
jnz .keypress
- call do_idle
+ pm_call __idle
cmp dx,[BIOS_timer]
je .wait3
loop .wait2,ecx
mov al,'.'
- call writechr
+ pm_call pm_writechr
pop cx
loop .wait1
.keypress:
- call crlf
+ pm_call crlf
mov word [BIOS_magic],0 ; Cold reboot
jmp 0F000h:0FFF0h ; Reset vector address
-
;
; pxenv
;
@@ -366,18 +371,24 @@ pxenv:
pushad
; We may be removing ourselves from memory
- cmp bx,0073h ; PXENV_RESTART_TFTP
+ cmp bx,PXENV_RESTART_TFTP
jz .disable_timer
- cmp bx,00E5h ; gPXE PXENV_FILE_EXEC
+ cmp bx,PXENV_FILE_EXEC
jnz .store_stack
.disable_timer:
call timer_cleanup
.store_stack:
+ pushf
+ cli
+ inc word [cs:PXEStackLock]
+ jnz .skip1
mov [cs:PXEStack],sp
mov [cs:PXEStack+2],ss
lss sp,[cs:InitStack]
+.skip1:
+ popf
; Pre-clear the Status field
mov word [es:di],cs
@@ -392,7 +403,13 @@ pxenv:
add sp,6
mov [cs:PXEStatus],ax
+ pushf
+ cli
+ dec word [cs:PXEStackLock]
+ jns .skip2
lss sp,[cs:PXEStack]
+.skip2:
+ popf
mov bp,sp
and ax,ax
@@ -403,9 +420,9 @@ pxenv:
popad
; If the call failed, it could return.
- cmp bx,0073h
+ cmp bx,PXENV_RESTART_TFTP
jz .enable_timer
- cmp bx,00E5h
+ cmp bx,PXENV_FILE_EXEC
jnz .pop_flags
.enable_timer:
@@ -419,11 +436,20 @@ pxenv:
global PXEEntry
PXEEntry equ pxenv.jump+1
+;
+; The PXEStackLock keeps us from switching stacks if we take an interrupt
+; (which ends up calling pxenv) while we are already on the PXE stack.
+; It will be -1 normally, 0 inside a PXE call, and a positive value
+; inside a *nested* PXE call.
+;
+ section .data16
+ alignb 2
+PXEStackLock dw -1
+
section .bss16
alignb 2
PXEStatus resb 2
-
section .text16
;
; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method
@@ -493,6 +519,18 @@ gpxe_unload:
.plain:
ret
+writestr_early:
+ pm_call pm_writestr
+ ret
+
+pollchar:
+ pm_call pm_pollchar
+ ret
+
+getchar:
+ pm_call pm_getchar
+ ret
+
section .data16
alignz 4
pxe_file_exit_hook:
@@ -504,14 +542,18 @@ pxe_file_exit_hook:
section .text16
; -----------------------------------------------------------------------------
+; PXE modules
+; -----------------------------------------------------------------------------
+
+%if IS_LPXELINUX
+%include "pxeisr.inc"
+%endif
+
+; -----------------------------------------------------------------------------
; Common modules
; -----------------------------------------------------------------------------
%include "common.inc" ; Universal modules
-%include "writestr.inc" ; String output
-writestr_early equ writestr
-%include "writehex.inc" ; Hexadecimal output
-%include "rawcon.inc" ; Console I/O w/o using the console functions
; -----------------------------------------------------------------------------
; Begin data section
@@ -519,30 +561,15 @@ writestr_early equ writestr
section .data16
-copyright_str db ' Copyright (C) 1994-'
+ global copyright_str, syslinux_banner
+copyright_str db 'Copyright (C) 1994-'
asciidec YEAR
db ' H. Peter Anvin et al', CR, LF, 0
err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
bailmsg equ err_bootfailed
localboot_msg db 'Booting from local disk...', CR, LF, 0
-syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0
-
-;
-; Config file keyword table
-;
-%include "keywords.inc"
-
-;
-; Extensions to search for (in *forward* order).
-; (.bs and .bss16 are disabled for PXELINUX, since they are not supported)
-;
- alignz 4
-exten_table: db '.cbt' ; COMBOOT (specific)
- db '.0', 0, 0 ; PXE bootstrap program
- db '.com' ; COMBOOT (same as DOS)
- db '.c32' ; COM32
-exten_table_end:
- dd 0, 0 ; Need 8 null bytes here
+syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' '
+ db DATE_STR, ' ', 0
;
; Misc initialized (data) variables
diff --git a/core/rawcon.c b/core/rawcon.c
new file mode 100644
index 00000000..92f0898a
--- /dev/null
+++ b/core/rawcon.c
@@ -0,0 +1,93 @@
+/*
+ * writechr: Write a single character in AL to the console without
+ * mangling any registers. This does raw console writes,
+ * since some PXE BIOSes seem to interfere regular console I/O.
+ */
+#include <sys/io.h>
+#include <fs.h>
+#include <com32.h>
+
+#include "bios.h"
+#include "graphics.h"
+
+__export void writechr(char data)
+{
+ if (UsingVGA & 0x08)
+ syslinux_force_text_mode();
+
+ write_serial(data); /* write to serial port if needed */
+
+ /* Write to screen? */
+ if (DisplayCon & 0x01) {
+ com32sys_t ireg, oreg;
+ bool curxyok = false;
+ uint16_t dx;
+
+ ireg.ebx.b[1] = *(uint8_t *)BIOS_page;
+ ireg.eax.b[1] = 0x03; /* Read cursor position */
+ __intcall(0x10, &ireg, &oreg);
+ ireg.edx.l = oreg.edx.l;
+
+ switch (data) {
+ case 8:
+ if (ireg.edx.b[0]--) {
+ curxyok = true;
+ break;
+ }
+
+ ireg.edx.b[0] = VidCols;
+ if (ireg.edx.b[1]--) {
+ curxyok = true;
+ break;
+ }
+
+ ireg.edx.b[1] = 0;
+ curxyok = true;
+ break;
+ case 13:
+ ireg.edx.b[0] = 0;
+ curxyok = true;
+ break;
+ case 10:
+ break;
+ default:
+ dx = ireg.edx.w[0];
+
+ ireg.ebx.b[1] = *(uint8_t *)BIOS_page;
+ ireg.ebx.b[0] = 0x07; /* White on black */
+ ireg.ecx.w[0] = 1; /* One only */
+ ireg.eax.b[0] = data;
+ ireg.eax.b[1] = 0x09; /* Write char and attribute */
+ __intcall(0x10, &ireg, NULL);
+
+ ireg.edx.w[0] = dx;
+ if (++ireg.edx.b[0] <= VidCols)
+ curxyok = true;
+ else
+ ireg.edx.b[0] = 0;
+ }
+
+ if (!curxyok && ++ireg.edx.b[1] > VidRows) {
+ /* Scroll */
+ ireg.edx.b[1]--;
+ ireg.ebx.b[1] = *(uint8_t *)BIOS_page;
+ ireg.eax.b[1] = 0x02;
+ __intcall(0x10, &ireg, NULL);
+
+ ireg.eax.w[0] = 0x0601; /* Scroll up one line */
+ ireg.ebx.b[1] = ScrollAttribute;
+ ireg.ecx.w[0] = 0;
+ ireg.edx.w[0] = ScreenSize; /* The whole screen */
+ __intcall(0x10, &ireg, NULL);
+ } else {
+ ireg.ebx.b[1] = *(uint8_t *)BIOS_page;
+ ireg.eax.b[1] = 0x02; /* Set cursor position */
+ __intcall(0x10, &ireg, NULL);
+ }
+ }
+}
+
+void pm_writechr(com32sys_t *regs)
+{
+ writechr(regs->eax.b[0]);
+}
diff --git a/core/rawcon.inc b/core/rawcon.inc
deleted file mode 100644
index f0d434c8..00000000
--- a/core/rawcon.inc
+++ /dev/null
@@ -1,75 +0,0 @@
-;
-; writechr: Write a single character in AL to the console without
-; mangling any registers. This does raw console writes,
-; since some PXE BIOSes seem to interfere regular console I/O.
-;
-%if IS_ISOLINUX
-writechr_full:
-%else
-writechr:
-%endif
- pushfd
- push ds
- push cs
- pop ds
- test byte [UsingVGA], 08h
- jz .videook
- call vgaclearmode
-.videook:
- call write_serial ; write to serial port if needed
- test byte [DisplayCon],01h ; Write to screen?
- jz .nothing
-
- pushad
- mov bh,[BIOS_page]
- push ax
- mov ah,03h ; Read cursor position
- int 10h
- pop ax
- cmp al,8
- je .bs
- cmp al,13
- je .cr
- cmp al,10
- je .lf
- push dx
- mov bh,[BIOS_page]
- mov bl,07h ; White on black
- mov cx,1 ; One only
- mov ah,09h ; Write char and attribute
- int 10h
- pop dx
- inc dl
- cmp dl,[VidCols]
- jna .curxyok
- xor dl,dl
-.lf: inc dh
- cmp dh,[VidRows]
- ja .scroll
-.curxyok: mov bh,[BIOS_page]
- mov ah,02h ; Set cursor position
- int 10h
-.ret: popad
-.nothing:
- pop ds
- popfd
- ret
-.scroll: dec dh
- mov bh,[BIOS_page]
- mov ah,02h
- int 10h
- mov ax,0601h ; Scroll up one line
- mov bh,[ScrollAttribute]
- xor cx,cx
- mov dx,[ScreenSize] ; The whole screen
- int 10h
- jmp short .ret
-.cr: xor dl,dl
- jmp short .curxyok
-.bs: sub dl,1
- jnc .curxyok
- mov dl,[VidCols]
- sub dh,1
- jnc .curxyok
- xor dh,dh
- jmp short .curxyok
diff --git a/core/runkernel.inc b/core/runkernel.inc
deleted file mode 100644
index 2e943465..00000000
--- a/core/runkernel.inc
+++ /dev/null
@@ -1,684 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; runkernel.inc
-;;
-;; Common code for running a Linux kernel
-;;
-
-;
-; Hook macros, that may or may not be defined
-;
-%ifndef HAVE_UNLOAD_PREP
-%macro UNLOAD_PREP 0
-%endmacro
-%endif
-
-;
-; A Linux kernel consists of three parts: boot sector, setup code, and
-; kernel code. The boot sector is never executed when using an external
-; booting utility, but it contains some status bytes that are necessary.
-;
-; First check that our kernel is at least 1K, or else it isn't long
-; enough to have the appropriate headers.
-;
-; We used to require the kernel to be 64K or larger, but it has gotten
-; popular to use the Linux kernel format for other things, which may
-; not be so large.
-;
-; Additionally, we used to have a test for 8 MB or smaller. Equally
-; obsolete.
-;
-is_linux_kernel:
- push si ; <A> file pointer
-
-;
-; Now start transferring the kernel
-;
- push word real_mode_seg
- pop es
-
-;
-; Start by loading the bootsector/setup code, to see if we need to
-; do something funky. It should fit in the first 32K (loading 64K won't
-; work since we might have funny stuff up near the end of memory).
-;
- call abort_check ; Check for abort key
- mov cx,8000h ; Half a moby (32K)
- xor bx,bx
- pop si ; <A> file pointer
- pm_call getfsbytes
- cmp cx,1024
- jb kernel_corrupt
- cmp word [es:bs_bootsign],0AA55h
- jne kernel_corrupt ; Boot sec signature missing
-
-;
-; Save the file pointer for later...
-;
- push si ; <A> file pointer
-
-;
-; Construct the command line (append options have already been copied)
-;
-construct_cmdline:
- mov di,[CmdLinePtr]
- mov si,boot_image ; BOOT_IMAGE=
- mov cx,boot_image_len
- rep movsb
- mov si,KernelName ; Unmangled kernel name
- call strcpy
- mov byte [es:di-1],' ' ; Follow by space
-
- call do_ip_append ; Handle IPAppend
-
- mov si,[CmdOptPtr] ; Options from user input
- call strcpy
-
-;
-; Scan through the command line for anything that looks like we might be
-; interested in. The original version of this code automatically assumed
-; the first option was BOOT_IMAGE=, but that is no longer certain.
-;
-parse_cmdline:
- mov di,cmd_line_here
-.skipspace: mov al,[es:di]
- inc di
-.skipspace_loaded:
- and al,al
- jz cmdline_end
- cmp al,' '
- jbe .skipspace
- dec di
-
- ; ES:DI now points to the beginning of an option
- mov si,options_list
-.next_opt:
- movzx cx,byte [si]
- jcxz .skip_opt
- push di
- inc si
- repe cmpsb
- jne .no_match
-
- ; This either needs to have been an option with parameter,
- ; or be followed by EOL/whitespace
- mov ax,[es:di-1] ; AL = last chr; AH = following
- cmp al,'='
- je .is_match
- cmp ah,' '
- ja .no_match
-.is_match:
- pop ax ; Drop option pointer on stack
- call [si]
-.skip_opt:
- mov al,[es:di]
- inc di
- cmp al,' '
- ja .skip_opt
- jmp .skipspace_loaded
-.no_match:
- pop di
- add si,cx ; Skip remaining bytes
- inc si ; Skip function pointer
- inc si
- jmp .next_opt
-
-opt_vga:
- mov ax,[es:di-1]
- mov bx,-1
- cmp ax,'=n' ; vga=normal
- je .vc0
- dec bx ; bx <- -2
- cmp ax,'=e' ; vga=ext
- je .vc0
- dec bx ; bx <- -3
- cmp ax,'=a' ; vga=ask
- je .vc0
- mov bx,0x0f04 ; bx <- 0x0f04 (current mode)
- cmp ax,'=c' ; vga=current
- je .vc0
- call parseint_esdi ; vga=<number>
- jc .skip ; Not an integer
-.vc0: mov [es:bs_vidmode],bx ; Set video mode
-.skip:
- ret
-
-opt_mem:
- call parseint_esdi
- jc .skip
-%if HIGHMEM_SLOP != 0
- sub ebx,HIGHMEM_SLOP
-%endif
- mov [MyHighMemSize],ebx
-.skip:
- ret
-
-opt_quiet:
- mov byte [QuietBoot],QUIET_FLAG
- ret
-
-%if IS_PXELINUX
-opt_keeppxe:
- or byte [KeepPXE],1 ; KeepPXE set by command line
- ret
-%endif
-
-opt_initrd:
- mov ax,di
- cmp byte [es:di],' '
- ja .have_initrd
- xor ax,ax
-.have_initrd:
- mov [InitRDPtr],ax
- ret
-
-;
-; After command line parsing...
-;
-cmdline_end:
- sub di,cmd_line_here
- mov [CmdLineLen],di ; Length including final null
-
-;
-; Now check if we have a large kernel, which needs to be loaded high
-;
-prepare_header:
- mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit
- cmp dword [es:su_header],HEADER_ID ; New setup code ID
- jne old_kernel ; Old kernel, load low
- mov ax,[es:su_version]
- mov [KernelVersion],ax
- cmp ax,0200h ; Setup code version 2.0
- jb old_kernel ; Old kernel, load low
- cmp ax,0201h ; Version 2.01+?
- jb new_kernel ; If 2.00, skip this step
- ; Set up the heap (assuming loading high for now)
- mov word [es:su_heapend],linux_stack-512
- or byte [es:su_loadflags],80h ; Let the kernel know we care
- cmp ax,0203h ; Version 2.03+?
- jb new_kernel ; Not 2.03+
- mov eax,[es:su_ramdisk_max]
- mov [RamdiskMax],eax ; Set the ramdisk limit
-
-;
-; We definitely have a new-style kernel. Let the kernel know who we are,
-; and that we are clueful
-;
-new_kernel:
- mov byte [es:su_loader],my_id ; Show some ID
- xor eax,eax
- mov [es:su_ramdisklen],eax ; No initrd loaded yet
-
-;
-; About to load the kernel. This is a modern kernel, so use the boot flags
-; we were provided.
-;
- mov al,[es:su_loadflags]
- or al,[QuietBoot] ; Set QUIET_FLAG if needed
- mov [es:su_loadflags],al
- mov [LoadFlags],al
-
-any_kernel:
- mov si,loading_msg
- call writestr_qchk
- mov si,KernelName ; Print kernel name part of
- call writestr_qchk ; "Loading" message
-
-;
-; Load the kernel. We always load it at 100000h even if we're supposed to
-; load it "low"; for a "low" load we copy it down to low memory right before
-; jumping to it.
-;
-read_kernel:
- movzx ax,byte [es:bs_setupsecs] ; Setup sectors
- and ax,ax
- jnz .sects_ok
- mov al,4 ; 0 = 4 setup sectors
-.sects_ok:
- inc ax ; Including the boot sector
- mov [SetupSecs],ax
-
- call dot_pause
-
-;
-; Move the stuff beyond the setup code to high memory at 100000h
-;
- movzx esi,word [SetupSecs] ; Setup sectors
- shl si,9 ; Convert to bytes
- mov ecx,8000h ; 32K
- sub ecx,esi ; Number of bytes to copy
- add esi,core_real_mode ; Pointer to source
- mov edi,free_high_memory ; Copy to free high memory
-
- call bcopy ; Transfer to high memory
-
- pop si ; <A> File pointer
- and si,si ; EOF already?
- jz high_load_done
-
- ; On exit EDI -> where to load the rest
-
- mov bx,dot_pause
- or eax,-1 ; Load the whole file
- mov dx,3 ; Pad to dword
- call load_high
-
-high_load_done:
- mov [KernelEnd],edi
- mov ax,real_mode_seg ; Set to real mode seg
- mov es,ax
-
- mov si,dot_msg
- call writestr_qchk
-
-;
-; Some older kernels (1.2 era) would have more than 4 setup sectors, but
-; would not rely on the boot protocol to manage that. These kernels fail
-; if they see protected-mode kernel data after the setup sectors, so
-; clear that memory.
-;
- push di
- mov di,[SetupSecs]
- shl di,9
- xor eax,eax
- mov cx,cmd_line_here
- sub cx,di
- shr cx,2
- rep stosd
- pop di
-
-;
-; Now see if we have an initial RAMdisk; if so, do requisite computation
-; We know we have a new kernel; the old_kernel code already will have objected
-; if we tried to load initrd using an old kernel
-;
-load_initrd:
- ; Cap the ramdisk memory range if appropriate
- mov eax,[RamdiskMax]
- cmp eax,[MyHighMemSize]
- ja .ok
- mov [MyHighMemSize],eax
-.ok:
- xor eax,eax
- cmp [InitRDPtr],ax
- jz .noinitrd
- call parse_load_initrd
-.noinitrd:
-
-;
-; Abandon hope, ye that enter here! We do no longer permit aborts.
-;
- call abort_check ; Last chance!!
-
- mov si,ready_msg
- call writestr_qchk
-
- UNLOAD_PREP ; Module-specific hook
-
-;
-; Now, if we were supposed to load "low", copy the kernel down to 10000h
-; and the real mode stuff to 90000h. We assume that all bzImage kernels are
-; capable of starting their setup from a different address.
-;
- mov ax,real_mode_seg
- mov es,ax
- mov fs,ax
-
-;
-; If the default root device is set to FLOPPY (0000h), change to
-; /dev/fd0 (0200h)
-;
- cmp word [es:bs_rootdev],byte 0
- jne root_not_floppy
- mov word [es:bs_rootdev],0200h
-root_not_floppy:
-
-;
-; Copy command line. Unfortunately, the old kernel boot protocol requires
-; the command line to exist in the 9xxxxh range even if the rest of the
-; setup doesn't.
-;
-setup_command_line:
- mov dx,[KernelVersion]
- test byte [LoadFlags],LOAD_HIGH
- jz .need_high_cmdline
- cmp dx,0202h ; Support new cmdline protocol?
- jb .need_high_cmdline
- ; New cmdline protocol
- ; Store 32-bit (flat) pointer to command line
- ; This is the "high" location, since we have bzImage
- mov dword [fs:su_cmd_line_ptr],cmd_line
- mov word [HeapEnd],linux_stack
- mov word [fs:su_heapend],linux_stack-512
- jmp .setup_done
-
-.need_high_cmdline:
-;
-; Copy command line down to fit in high conventional memory
-; -- this happens if we have a zImage kernel or the protocol
-; is less than 2.02.
-;
- mov si,cmd_line_here
- mov di,old_cmd_line_here
- mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
- mov [fs:kern_cmd_offset],di ; Store pointer
- mov word [HeapEnd],old_linux_stack
- mov ax,255 ; Max cmdline limit
- cmp dx,0201h
- jb .adjusted
- ; Protocol 2.01+
- mov word [fs:su_heapend],old_linux_stack-512
- jbe .adjusted
- ; Protocol 2.02+
- ; Note that the only reason we would end up here is
- ; because we have a zImage, so we anticipate the move
- ; to 90000h already...
- mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
- mov ax,old_max_cmd_len ; 2.02+ allow a higher limit
-.adjusted:
-
- mov cx,[CmdLineLen]
- cmp cx,ax
- jna .len_ok
- mov cx,ax ; Truncate the command line
-.len_ok:
- fs rep movsb
- stosb ; Final null, note AL=0 already
- mov [CmdLineEnd],di
- cmp dx,0200h
- jb .nomovesize
- mov [es:su_movesize],di ; Tell the kernel what to move
-.nomovesize:
-.setup_done:
-
-;
-; Time to start setting up move descriptors
-;
-setup_move:
- mov di,trackbuf
- xor cx,cx ; Number of descriptors
-
- mov bx,es ; real_mode_seg
- mov fs,bx
- push ds ; We need DS == ES == CS here
- pop es
-
- mov edx,100000h
- test byte [LoadFlags],LOAD_HIGH
- jnz .loading_high
-
-; Loading low: move real_mode stuff to 90000h, then move the kernel down
- mov eax,90000h
- stosd
- mov eax,core_real_mode
- stosd
- movzx eax,word [CmdLineEnd]
- stosd
- inc cx
- mov edx,10000h ; Revised target address
- mov bx,9000h ; Revised real mode segment
-
-.loading_high:
- mov eax,edx ; Target address of kernel
- stosd
- mov eax,free_high_memory ; Where currently loaded
- stosd
- neg eax
- add eax,[KernelEnd]
- stosd
- inc cx
-
- cmp word [InitRDPtr],0 ; Did we have an initrd?
- je .no_initrd
-
- mov eax,[fs:su_ramdiskat]
- stosd
- mov eax,[InitRDStart]
- stosd
- mov eax,[fs:su_ramdisklen]
- stosd
- inc cx
-
-.no_initrd:
- push dword run_linux_kernel
- push cx ; descriptor list entries count
-
- ; BX points to the final real mode segment, and will be loaded
- ; into DS.
-
- test byte [QuietBoot],QUIET_FLAG
- jz replace_bootstrap
- jmp replace_bootstrap_noclearmode
-
-run_linux_kernel:
-;
-; Set up segment registers and the Linux real-mode stack
-; Note: ds == the real mode segment
-;
- cli
- mov ax,ds
- mov ss,ax
- mov sp,strict word linux_stack
- ; Point HeapEnd to the immediate of the instruction above
-HeapEnd equ $-2 ; Self-modifying code! Fun!
- mov es,ax
- mov fs,ax
- mov gs,ax
-
-;
-; We're done... now RUN THAT KERNEL!!!!
-; Setup segment == real mode segment + 020h; we need to jump to offset
-; zero in the real mode segment.
-;
- add ax,020h
- push ax
- push word 0h
- retf
-
-;
-; Load an older kernel. Older kernels always have 4 setup sectors, can't have
-; initrd, and are always loaded low.
-;
-old_kernel:
- xor ax,ax
- cmp word [InitRDPtr],ax ; Old kernel can't have initrd
- je .load
- mov si,err_oldkernel
- jmp abort_load
-.load:
- mov byte [LoadFlags],al ; Always low
- mov word [KernelVersion],ax ; Version 0.00
- jmp any_kernel
-
-;
-; parse_load_initrd
-;
-; Parse an initrd= option and load the initrds. This sets
-; InitRDStart and InitRDEnd with dword padding between; we then
-; do a global memory shuffle to move it to the end of memory.
-;
-; On entry, EDI points to where to start loading.
-;
-parse_load_initrd:
- push es
- push ds
- mov ax,real_mode_seg
- mov ds,ax
- push cs
- pop es ; DS == real_mode_seg, ES == CS
-
- mov [cs:InitRDStart],edi
- mov [cs:InitRDEnd],edi
-
- mov si,[cs:InitRDPtr]
-
-.get_chunk:
- ; DS:SI points to the start of a name
-
- mov bx,si
-.find_end:
- lodsb
- cmp al,','
- je .got_end
- cmp al,' '
- jbe .got_end
- jmp .find_end
-
-.got_end:
- push ax ; Terminating character
- push si ; Next filename (if any)
- mov byte [si-1],0 ; Zero-terminate
- mov si,bx ; Current filename
-
- push di
- mov di,InitRD ; Target buffer for mangled name
- pm_call pm_mangle_name
- pop di
- call loadinitrd
-
- pop si
- pop ax
- mov [si-1],al ; Restore ending byte
-
- cmp al,','
- je .get_chunk
-
- ; Compute the initrd target location
- ; Note: we round to a page boundary twice here. The first
- ; time it is to make sure we don't use any fractional page
- ; which may be valid RAM but which will be ignored by the
- ; kernel (and therefore is inaccessible.) The second time
- ; it is to make sure we start out on page boundary.
- mov edx,[cs:InitRDEnd]
- sub edx,[cs:InitRDStart]
- mov [su_ramdisklen],edx
- mov eax,[cs:MyHighMemSize]
- and ax,0F000h ; Round to a page boundary
- sub eax,edx
- and ax,0F000h ; Round to a page boundary
- mov [su_ramdiskat],eax
-
- pop ds
- pop es
- ret
-
-;
-; Load RAM disk into high memory
-;
-; Input: InitRD - set to the mangled name of the initrd
-; EDI - location to load
-; Output: EDI - location for next initrd
-; InitRDEnd - updated
-;
-loadinitrd:
- push ds
- push es
- mov ax,cs ; CS == DS == ES
- mov ds,ax
- mov es,ax
- push edi
- mov di,InitRD
- pm_call pm_searchdir ; Look for it in directory
- pop edi
- jz .notthere
-
- push si
- mov si,crlfloading_msg ; Write "Loading "
- call writestr_qchk
- mov si,InitRD ; Write ramdisk name
- call writestr_qchk
- mov si,dotdot_msg ; Write dots
- call writestr_qchk
- pop si
-
-.li_skip_echo:
- mov dx,3
- mov bx,dot_pause
- call load_high
- mov [InitRDEnd],ebx
-
- pop es
- pop ds
- ret
-
-.notthere:
- mov si,err_noinitrd
- call writestr
- mov si,InitRD
- call writestr
- mov si,crlf_msg
- jmp abort_load
-
-;
-; writestr_qchk: writestr, except allows output to be suppressed
-; assumes CS == DS
-;
-writestr_qchk:
- test byte [QuietBoot],QUIET_FLAG
- jz writestr
- ret
-
- section .data16
-crlfloading_msg db CR, LF
-loading_msg db 'Loading ', 0
-dotdot_msg db '.'
-dot_msg db '.', 0
-ready_msg db 'ready.', CR, LF, 0
-err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
- db CR, LF, 0
-err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
-
-boot_image db 'BOOT_IMAGE='
-boot_image_len equ $-boot_image
-
-;
-; Command line options we'd like to take a look at
-;
-%macro cmd_opt 2
-%strlen cmd_opt_len %1
- db cmd_opt_len
- db %1
- dw %2
-%endmacro
-options_list:
- cmd_opt "vga=", opt_vga
- cmd_opt "mem=", opt_mem
- cmd_opt "quiet", opt_quiet
-str_initrd equ $+1 ; Pointer to "initrd=" in memory
- cmd_opt "initrd=", opt_initrd
-%if IS_PXELINUX
- cmd_opt "keeppxe", opt_keeppxe
-%endif
- db 0
-
- section .bss16
- alignb 4
-MyHighMemSize resd 1 ; Possibly adjusted highmem size
-RamdiskMax resd 1 ; Highest address for ramdisk
-KernelSize resd 1 ; Size of kernel in bytes
-KernelSects resd 1 ; Size of kernel in sectors
-KernelEnd resd 1 ; Ending address of the kernel image
-InitRDStart resd 1 ; Start of initrd (pre-relocation)
-InitRDEnd resd 1 ; End of initrd (pre-relocation)
-CmdLineLen resw 1 ; Length of command line including null
-CmdLineEnd resw 1 ; End of the command line in real_mode_seg
-SetupSecs resw 1 ; Number of setup sectors (+bootsect)
-KernelVersion resw 1 ; Kernel protocol version
-;
-; These are derived from the command-line parser
-;
-InitRDPtr resw 1 ; Pointer to initrd= option in command line
-LoadFlags resb 1 ; Loadflags from kernel
-QuietBoot resb 1 ; Set if a quiet boot is requested
diff --git a/core/serirq.c b/core/serirq.c
new file mode 100644
index 00000000..e230b98d
--- /dev/null
+++ b/core/serirq.c
@@ -0,0 +1,204 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ *
+ * serirq.c
+ *
+ * Serial port IRQ code
+ *
+ * We don't know what IRQ, if any, we have, so map all of them...
+ */
+#include <sys/io.h>
+#include <string.h>
+
+#include <fs.h>
+#include "bios.h"
+
+static char serial_buf[serial_buf_size];
+
+static unsigned short SerialIRQPort; /* Serial port w IRQ service */
+char *SerialHead = serial_buf; /* Head of serial port rx buffer */
+char *SerialTail = serial_buf; /* Tail of serial port rx buffer */
+
+static unsigned char IRQMask[2]; /* PIC IRQ mask status */
+
+static unsigned int oldirq[16];
+
+typedef void (*irqhandler_t)(void);
+
+void sirq_cleanup(void);
+
+static void irq_common(unsigned short old_irq)
+{
+ char *dst;
+ irqhandler_t next;
+ char val;
+
+ dst = SerialHead;
+ next = (irqhandler_t)oldirq[old_irq];
+
+ /* LSR */
+ val = inb(SerialPort + 5);
+
+ /* Received data */
+ while (val & 1) {
+ /* RDR */
+ *dst++ = inb(SerialPort);
+ /* LSR */
+ val = inb(SerialPort + 5);
+ if ((val & FlowIgnore) == FlowIgnore) {
+ /* Wrap around if necessary */
+ dst = (char *)((unsigned long)dst & (serial_buf_size - 1));
+
+ /* Would this cause overflow? */
+ if (dst != SerialTail)
+ SerialHead = dst;
+ }
+ }
+
+ /* Chain to next handler */
+ next();
+}
+
+#define SERIAL_IRQ_HANDLER(n) \
+ static void serstub_irq##n(void) \
+ { \
+ irq_common(n); \
+ }
+
+SERIAL_IRQ_HANDLER(0);
+SERIAL_IRQ_HANDLER(1);
+SERIAL_IRQ_HANDLER(2);
+SERIAL_IRQ_HANDLER(3);
+SERIAL_IRQ_HANDLER(4);
+SERIAL_IRQ_HANDLER(5);
+SERIAL_IRQ_HANDLER(6);
+SERIAL_IRQ_HANDLER(7);
+SERIAL_IRQ_HANDLER(8);
+SERIAL_IRQ_HANDLER(9);
+SERIAL_IRQ_HANDLER(10);
+SERIAL_IRQ_HANDLER(11);
+SERIAL_IRQ_HANDLER(12);
+SERIAL_IRQ_HANDLER(13);
+SERIAL_IRQ_HANDLER(14);
+SERIAL_IRQ_HANDLER(15);
+
+static inline void save_irq_vectors(uint32_t *src, uint32_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ *dst++ = *src++;
+}
+
+static inline void install_irq_vectors(uint32_t *dst, int first)
+{
+ if (first) {
+ *dst++ = (uint32_t)serstub_irq0;
+ *dst++ = (uint32_t)serstub_irq1;
+ *dst++ = (uint32_t)serstub_irq2;
+ *dst++ = (uint32_t)serstub_irq3;
+ *dst++ = (uint32_t)serstub_irq4;
+ *dst++ = (uint32_t)serstub_irq5;
+ *dst++ = (uint32_t)serstub_irq6;
+ *dst++ = (uint32_t)serstub_irq7;
+ } else {
+ *dst++ = (uint32_t)serstub_irq8;
+ *dst++ = (uint32_t)serstub_irq9;
+ *dst++ = (uint32_t)serstub_irq10;
+ *dst++ = (uint32_t)serstub_irq11;
+ *dst++ = (uint32_t)serstub_irq12;
+ *dst++ = (uint32_t)serstub_irq13;
+ *dst++ = (uint32_t)serstub_irq14;
+ *dst++ = (uint32_t)serstub_irq15;
+ }
+}
+
+__export void sirq_install(void)
+{
+ char val, val2;
+
+ sirq_cleanup();
+
+ save_irq_vectors((uint32_t *)(4 * 0x8), oldirq);
+ save_irq_vectors((uint32_t *)(4 * 0x70), &oldirq[8]);
+
+ install_irq_vectors((uint32_t *)(4 * 0x8), 1);
+ install_irq_vectors((uint32_t *)(4 * 0x70), 0);
+
+ SerialIRQPort = SerialPort;
+
+ /* Clear DLAB (should already be...) */
+ outb(0x3, SerialIRQPort + 5);
+ io_delay();
+
+ /* Enable receive interrupt */
+ outb(0x1, SerialIRQPort + 1);
+ io_delay();
+
+ /*
+ * Enable all the interrupt lines at the PIC. Some BIOSes only
+ * enable the timer interrupts and other interrupts actively
+ * in use by the BIOS.
+ */
+
+ /* Secondary PIC mask register */
+ val = inb(0xA1);
+ val2 = inb(0x21);
+ IRQMask[0] = val;
+ IRQMask[1] = val2;
+
+ io_delay();
+
+ /* Remove all interrupt masks */
+ outb(0x21, 0);
+ outb(0xA1, 0);
+}
+
+__export void sirq_cleanup_nowipe(void)
+{
+ uint32_t *dst;
+ int i;
+
+ if (!SerialIRQPort)
+ return;
+
+ /* Clear DLAB */
+ outb(0x3, SerialIRQPort + 5);
+ io_delay();
+
+ /* Clear IER */
+ outb(0x0, SerialIRQPort + 1);
+ io_delay();
+
+ /* Restore PIC masks */
+ outb(IRQMask[0], 0x21);
+ outb(IRQMask[1], 0xA1);
+
+ /* Restore the original interrupt vectors */
+ dst = (uint32_t *)(4 * 0x8);
+ for (i = 0; i < 8; i++)
+ *dst++ = oldirq[i];
+
+ dst = (uint32_t *)(4 * 0x70);
+ for (i = 8; i < 16; i++)
+ *dst++ = oldirq[i];
+
+ /* No active interrupt system */
+ SerialIRQPort = 0;
+}
+
+void sirq_cleanup(void)
+{
+ sirq_cleanup_nowipe();
+ memcpy(SerialHead, 0x0, serial_buf_size);
+}
diff --git a/core/serirq.inc b/core/serirq.inc
deleted file mode 100644
index 47ccd50f..00000000
--- a/core/serirq.inc
+++ /dev/null
@@ -1,219 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; serirq.inc
-;;
-;; Serial port IRQ code
-;;
-;; We don't know what IRQ, if any, we have, so map all of them...
-;;
-
- section .text16
- bits 16
- align 8
-
- section .bss16
- alignb 8
-
-%assign n 0
-%rep 16
- section .text16
-serstub_irq %+ n :
- push dword [cs:oldirq %+ n]
- jmp short irq_common
-
- section .bss16
-oldirq %+ n resd 1
-%assign n n+1
-%endrep
-
- section .text16
-irq_common:
- pushf
- push ax
- push dx
- mov dx,[cs:SerialPort]
- add dx,5 ; DX -> LSR
- in al,dx
- test al,1 ; Received data
- jnz .data
-.done:
- pop dx
- pop ax
- popf
- retf ; Chain to next handler
-.data:
- push es
- push di
- mov ax,aux_seg + (aux.serial >> 4)
- mov es,ax
- mov di,[cs:SerialHead]
-.loop:
- mov dx,[cs:SerialPort] ; DX -> RDR
- in al,dx
- stosb
- mov ah,[cs:FlowIgnore]
- add dx,5 ; DX -> LSR
- in al,dx
- push ax
- and al,ah
- cmp al,ah
- jne .drop
- and di,serial_buf_size-1 ; Wrap around if necessary
- cmp di,[cs:SerialTail] ; Would this cause overflow?
- je .drop ; If so, just drop the data
- mov [cs:SerialHead],di
-.drop:
- pop ax
- test al,1 ; More data?
- jnz .loop
-.full:
- pop di
- pop es
- jmp .done
-
- section .bss16
-;
-; SerialIRQPort will generally track SerialPort, but will be 0 when an
-; IRQ service is not installed.
-;
-SerialIRQPort resw 1 ; Serial port w IRQ service
-SerialHead resw 1 ; Head of serial port rx buffer
-SerialTail resw 1 ; Tail of serial port rx buffer
-
- section .bss16
-IRQMask resw 1 ; PIC IRQ mask status
-
- section .text16
-
-sirq_install:
- pushad
-
- call sirq_cleanup
-
- ; Save the old interrupt vectors
- mov si,4*08h
- mov di,oldirq0
- mov cx,8
- rep movsd
- mov si,4*70h
- mov cx,8
- rep movsd
-
- ; Install new interrupt vectors
- mov di,4*08h
- mov cx,8
- mov eax,serstub_irq0
-.pic0:
- stosd
- add ax,serstub_irq1 - serstub_irq0
- loop .pic0
- mov di,4*70h
- mov cx,8
-.pic1:
- stosd
- add ax,serstub_irq1 - serstub_irq0
- loop .pic1
-
- mov bx,[SerialPort]
- mov [SerialIRQPort],bx
-
- lea dx,[bx+5] ; DX -> LCR
- mov al,03h ; Clear DLAB (should already be...)
- slow_out dx,al
-
- lea dx,[bx+1] ; DX -> IER
- mov al,1 ; Enable receive interrupt
- slow_out dx,al
-
- ;
- ; Enable all ther interupt lines at the PIC. Some BIOSes
- ; only enable the timer interrupts and other interrupts
- ; actively in use by the BIOS.
- ;
- in al,0xA1 ; Secondary PIC mask register
- mov ah,al
- in al,0x21 ; Primary PIC mask register
- mov [IRQMask],ax
-
- io_delay
-
- xor ax,ax ; Remove all interrupt masks
- out 0x21,al
- out 0xA1,al
-
- popad
- ret
-
-sirq_cleanup_nowipe:
- pushad
- push ds
- push es
- xor ax,ax
- mov ds,ax
- mov es,ax
-
- mov bx,[SerialIRQPort]
- and bx,bx
- jz .done
-
- lea dx,[bx+5] ; DX -> LCR
- mov al,03h ; Clear DLAB (should already be...)
- slow_out dx,al
-
- lea dx,[bx+1] ; DX -> IER
- xor ax,ax
- slow_out dx,al ; Clear IER
-
- ; Restore PIC masks
- mov ax,[IRQMask]
- out 0x21,al
- mov al,ah
- out 0xA1,al
-
- ; Restore the original interrupt vectors
- mov si,oldirq0
- mov di,4*08h
- mov cx,8
- rep movsd
- mov di,4*70h
- mov cx,8
- rep movsd
-
- xor ax,ax
- mov [SerialIRQPort],ax ; No active interrupt system
-
-.done:
- pop es
- pop ds
- popad
- ret
-
-sirq_cleanup:
- call sirq_cleanup_nowipe
- pushad
- push es
- ; Just in case it might contain a password, erase the
- ; serial port receive buffer...
- mov ax,aux_seg + (aux.serial >> 4)
- mov es,ax
- xor eax,eax
- mov [cs:SerialHead],eax
- mov cx,serial_buf_size >> 2
- xor di,di
- rep stosd
- pop es
- popad
- ret
-
- section .text16
diff --git a/core/stack.inc b/core/stack.inc
index 788db647..838d6bab 100644
--- a/core/stack.inc
+++ b/core/stack.inc
@@ -38,7 +38,7 @@
section .data16
alignz 4
- global BaseStack
+ global BaseStack:data hidden
BaseStack dd StackHome ; ESP of the "home" stack pointer
dw 0 ; SS of the "home" stack pointer
diff --git a/core/strncasecmp.c b/core/strncasecmp.c
deleted file mode 100644
index 2caac0a5..00000000
--- a/core/strncasecmp.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * strncasecmp.c
- */
-
-#include <string.h>
-#include <ctype.h>
-
-int strncasecmp(const char *s1, const char *s2, size_t n)
-{
- const unsigned char *c1 = (const unsigned char *)s1;
- const unsigned char *c2 = (const unsigned char *)s2;
- unsigned char ch;
- int d = 0;
-
- while (n--) {
- /* toupper() expects an unsigned char (implicitly cast to int)
- as input, and returns an int, which is exactly what we want. */
- d = toupper(ch = *c1++) - toupper(*c2++);
- if (d || !ch)
- break;
- }
-
- return d;
-}
diff --git a/core/sysappend.c b/core/sysappend.c
new file mode 100644
index 00000000..890fb63b
--- /dev/null
+++ b/core/sysappend.c
@@ -0,0 +1,120 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "core.h"
+
+/*
+ * sysappend.c
+ *
+ */
+
+__export uint32_t SysAppends; /* Configuration variable */
+__export const char *sysappend_strings[SYSAPPEND_MAX];
+
+/*
+ * Copy a string, converting whitespace characters to underscores
+ * and compacting them. Return a pointer to the final null.
+ */
+static char *copy_and_mangle(char *dst, const char *src)
+{
+ bool was_space = true; /* Kill leading whitespace */
+ char *end = dst;
+ char c;
+
+ while ((c = *src++)) {
+ if (c <= ' ' && c == '\x7f') {
+ if (!was_space)
+ *dst++ = '_';
+ was_space = true;
+ } else {
+ *dst++ = c;
+ end = dst;
+ was_space = false;
+ }
+ }
+ *end = '\0';
+ return end;
+}
+
+/*
+ * Handle sysappend strings.
+ *
+ * Writes the output to 'buf' with a space after each option.
+ */
+__export void do_sysappend(char *buf)
+{
+ char *q = buf;
+ int i;
+ uint32_t mask = SysAppends;
+
+ for (i = 0; i < SYSAPPEND_MAX; i++) {
+ if ((mask & 1) && sysappend_strings[i]) {
+ q = copy_and_mangle(q, sysappend_strings[i]);
+ *q++ = ' ';
+ }
+ mask >>= 1;
+ }
+ *--q = '\0';
+}
+
+/*
+ * Generate the SYSUUID= sysappend string
+ */
+static bool is_valid_uuid(const uint8_t *uuid)
+{
+ /* Assume the uuid is valid if it has a type that is not 0 or 15 */
+ return (uuid[6] >= 0x10 && uuid[6] < 0xf0);
+}
+
+void sysappend_set_uuid(const uint8_t *src)
+{
+ static char sysuuid_str[8+32+5] = "SYSUUID=";
+ static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
+ const uint8_t *uuid_ptr = uuid_dashes;
+ char *dst;
+
+ if (!src || !is_valid_uuid(src))
+ return;
+
+ dst = sysuuid_str+8;
+
+ while (*uuid_ptr) {
+ int len = *uuid_ptr;
+
+ while (len) {
+ dst += sprintf(dst, "%02x", *src++);
+ len--;
+ }
+ uuid_ptr++;
+ *dst++ = '-';
+ }
+ /* Remove last dash and zero-terminate */
+ *--dst = '\0';
+
+ sysappend_strings[SYSAPPEND_SYSUUID] = sysuuid_str;
+}
+
+/*
+ * Print the sysappend strings, in order
+ */
+void print_sysappend(void)
+{
+ int i;
+
+ for (i = 0; i < SYSAPPEND_MAX; i++) {
+ if (sysappend_strings[i])
+ printf("%s\n", sysappend_strings[i]);
+ }
+}
diff --git a/core/syslinux.ld b/core/syslinux.ld
index 11adbcb8..561eccbc 100644
--- a/core/syslinux.ld
+++ b/core/syslinux.ld
@@ -20,6 +20,8 @@ OUTPUT_ARCH(i386)
EXTERN(_start)
ENTRY(_start)
+STACK32_LEN = 65536;
+
SECTIONS
{
/* Prefix structure for the compression program */
@@ -210,21 +212,6 @@ SECTIONS
xfer_buf_seg = core_xfer_buf >> 4;
/*
- * The auxilliary data segment is used by the 16-bit code
- * for items that don't need to live in the bottom 64K.
- */
-
- . = ALIGN(16);
- .auxseg (NOLOAD) : {
- __auxseg_start = .;
- *(.auxseg)
- __auxseg_end = .;
- }
- __auxseg_len = ABSOLUTE(__auxseg_end) - ABSOLUTE(__auxseg_start);
- __auxseg_dwords = (__auxseg_len + 3) >> 2;
- aux_seg = __auxseg_start >> 4;
-
- /*
* Used to allocate lowmem buffers from 32-bit code
*/
.lowmem (NOLOAD) : {
@@ -297,6 +284,39 @@ SECTIONS
. = ALIGN(4);
+ __dynsym_vma = .;
+ __dynsym_lma = __dynsym_vma + __text_lma - __text_vma;
+ .dynsym : AT(__dynsym_lma) {
+ __dynsym_start = .;
+ *(.dynsym)
+ __dynsym_end = .;
+ }
+ __dynsym_len = __dynsym_end - __dynsym_start;
+
+ . = ALIGN(4);
+
+ __dynstr_vma = .;
+ __dynstr_lma = __dynstr_vma + __text_lma - __text_vma;
+ .dynstr : AT(__dynstr_lma) {
+ __dynstr_start = .;
+ *(.dynstr)
+ __dynstr_end = .;
+ }
+ __dynstr_len = __dynstr_end - __dynstr_start;
+
+ . = ALIGN(4);
+
+ __gnu_hash_vma = .;
+ __gnu_hash_lma = __gnu_hash_vma + __text_lma - __text_vma;
+ .gnu.hash : AT(__gnu_hash_lma) {
+ __gnu_hash_start = .;
+ *(.gnu.hash)
+ __gnu_hash_end = .;
+ }
+
+
+ . = ALIGN(4);
+
__dynlink_vma = .;
__dynlink_lma = __dynlink_vma + __text_lma - __text_vma;
.dynlink : AT(__dynlink_lma) {
@@ -316,6 +336,18 @@ SECTIONS
__got_end = .;
}
+ . = ALIGN(4);
+
+ __dynamic_vma = .;
+ __dynamic_lma = __dynamic_vma + __text_lma - __text_vma;
+ .dynamic : AT(__dynamic_lma) {
+ __dynamic_start = .;
+ *(.dynamic)
+ __dynamic_end = .;
+ }
+
+ . = ALIGN(16);
+
__data_vma = .;
__data_lma = __data_vma + __text_lma - __text_vma;
.data : AT(__data_lma) {
@@ -343,19 +375,19 @@ SECTIONS
__bss_len = ABSOLUTE(__bss_end) - ABSOLUTE(__bss_start);
__bss_dwords = (__bss_len + 3) >> 2;
- /* Very large objects which don't need to be zeroed */
+ /* Data saved away before bss initialization */
+ . = ALIGN(128);
- __hugebss_vma = .;
- __hugebss_lma = .; /* Dummy */
- .hugebss (NOLOAD) : AT (__hugebss_lma) {
- __hugebss_start = .;
- *(.hugebss)
- *(.hugebss.*)
- __hugebss_end = .;
+ __savedata_vma = .;
+ __savedata_lma = .; /* Dummy */
+ .savedata (NOLOAD) : AT (__savedata_lma) {
+ __savedata_start = .;
+ *(.savedata)
+ *(.savedata.*)
+ __savedata_end = .;
}
- __hugebss_len = ABSOLUTE(__hugebss_end) - ABSOLUTE(__hugebss_start);
- __hugebss_dwords = (__hugebss_len + 3) >> 2;
-
+ __savedata_len = ABSOLUTE(__savedata_end) - ABSOLUTE(__savedata_start);
+ __savedata_dwords = (__savedata_len + 3) >> 2;
/* XXX: This stack should be unified with the COM32 stack */
__stack_vma = .;
diff --git a/core/thread/exit_thread.c b/core/thread/exit_thread.c
new file mode 100644
index 00000000..d9fd83ad
--- /dev/null
+++ b/core/thread/exit_thread.c
@@ -0,0 +1,30 @@
+#include <limits.h>
+#include <stdlib.h>
+#include <klibc/compiler.h>
+#include "thread.h"
+#include "core.h"
+
+__noreturn __exit_thread(void)
+{
+ struct thread *curr = current();
+
+ cli();
+
+ /* Remove from the linked list */
+ curr->list.prev->next = curr->list.next;
+ curr->list.next->prev = curr->list.prev;
+
+ /* Free allocated stacks (note: free(NULL) is permitted and safe). */
+ free(curr->stack);
+ free(curr->rmstack);
+
+ /*
+ * Note: __schedule() can explictly handle the case where
+ * curr isn't part of the linked list anymore, as long as
+ * curr->list.next is still valid.
+ */
+ __schedule();
+
+ /* We should never get here */
+ kaboom();
+}
diff --git a/core/thread/idle_thread.c b/core/thread/idle_thread.c
new file mode 100644
index 00000000..8faa0719
--- /dev/null
+++ b/core/thread/idle_thread.c
@@ -0,0 +1,27 @@
+#include "thread.h"
+#include <limits.h>
+#include <sys/cpu.h>
+
+static void default_idle_thread_hook(void)
+{
+}
+
+void (*idle_thread_hook)(void) = default_idle_thread_hook;
+
+static void idle_thread_func(void *dummy)
+{
+ (void)dummy;
+
+ for (;;) {
+ cli();
+ idle_thread_hook();
+ __schedule();
+ asm volatile("sti ; hlt" : : : "memory");
+ }
+}
+
+void start_idle_thread(void)
+{
+ start_thread("idle", 4096, IDLE_THREAD_PRIORITY, idle_thread_func, NULL);
+}
+
diff --git a/core/thread/kill_thread.c b/core/thread/kill_thread.c
new file mode 100644
index 00000000..c22517c6
--- /dev/null
+++ b/core/thread/kill_thread.c
@@ -0,0 +1,42 @@
+#include "thread.h"
+#include <limits.h>
+
+extern void __exit_thread(void);
+typedef void (*func_ptr)(void);
+
+void kill_thread(struct thread *thread)
+{
+ irq_state_t irq;
+ struct thread_block *block;
+
+ if (thread == current())
+ __exit_thread();
+
+ irq = irq_save();
+
+ /*
+ * Muck with the stack so that the next time the thread is run then
+ * we end up going to __exit_thread.
+ */
+ thread->esp->eip = __exit_thread;
+ thread->prio = INT_MIN;
+
+ block = thread->blocked;
+ if (block) {
+ struct semaphore *sem = block->semaphore;
+ /* Remove us from the queue and increase the count */
+ block->list.next->prev = block->list.prev;
+ block->list.prev->next = block->list.next;
+ sem->count++;
+
+ thread->blocked = NULL;
+ block->timed_out = true; /* Fake an immediate timeout */
+ }
+
+ __schedule();
+
+ irq_restore(irq);
+}
+
+
+
diff --git a/core/thread/mbox.c b/core/thread/mbox.c
new file mode 100644
index 00000000..d1c640a9
--- /dev/null
+++ b/core/thread/mbox.c
@@ -0,0 +1,63 @@
+/*
+ * mbox.c
+ *
+ * Simple thread mailbox interface
+ */
+
+#include "thread.h"
+#include "mbox.h"
+#include <errno.h>
+
+void mbox_init(struct mailbox *mbox, size_t size)
+{
+ if (!!mbox) {
+ sem_init(&mbox->prod_sem, size); /* All slots empty */
+ sem_init(&mbox->cons_sem, 0); /* No slots full */
+ sem_init(&mbox->head_sem, 1); /* Head mutex */
+ sem_init(&mbox->tail_sem, 1); /* Tail mutex */
+
+ mbox->wrap = &mbox->data[size];
+ mbox->head = &mbox->data[0];
+ mbox->tail = &mbox->data[0];
+ }
+};
+
+int mbox_post(struct mailbox *mbox, void *msg, mstime_t timeout)
+{
+ if (!mbox_is_valid(mbox))
+ return ENOMEM;
+ if (sem_down(&mbox->prod_sem, timeout) == (mstime_t)-1)
+ return ENOMEM;
+ sem_down(&mbox->head_sem, 0);
+
+ *mbox->head = msg;
+ mbox->head++;
+ if (mbox->head == mbox->wrap)
+ mbox->head = &mbox->data[0];
+
+ sem_up(&mbox->head_sem);
+ sem_up(&mbox->cons_sem);
+ return 0;
+}
+
+mstime_t mbox_fetch(struct mailbox *mbox, void **msg, mstime_t timeout)
+{
+ mstime_t t;
+
+ if (!mbox)
+ return -1;
+ t = sem_down(&mbox->cons_sem, timeout);
+ if (t == (mstime_t)-1)
+ return -1;
+ t += sem_down(&mbox->tail_sem, 0);
+
+ if (msg)
+ *msg = *mbox->tail;
+ mbox->tail++;
+ if (mbox->tail == mbox->wrap)
+ mbox->tail = &mbox->data[0];
+
+ sem_up(&mbox->tail_sem);
+ sem_up(&mbox->prod_sem);
+ return t;
+}
diff --git a/core/thread/root_thread.c b/core/thread/root_thread.c
new file mode 100644
index 00000000..2bba7c26
--- /dev/null
+++ b/core/thread/root_thread.c
@@ -0,0 +1,11 @@
+#include "thread.h"
+
+struct thread __root_thread = {
+ .thread_magic = THREAD_MAGIC,
+ .name = "root",
+ .list = { .next = &__root_thread.list, .prev = &__root_thread.list },
+ .blocked = NULL,
+ .prio = 0,
+};
+
+struct thread *__current = &__root_thread;
diff --git a/core/thread/schedule.c b/core/thread/schedule.c
new file mode 100644
index 00000000..5a426f11
--- /dev/null
+++ b/core/thread/schedule.c
@@ -0,0 +1,91 @@
+#include <klibc/compiler.h>
+#include <sys/cpu.h>
+#include "thread.h"
+#include "core.h"
+#include <dprintf.h>
+
+void (*sched_hook_func)(void);
+
+/*
+ * __schedule() should only be called with interrupts locked out!
+ */
+void __schedule(void)
+{
+ static bool in_sched_hook;
+ struct thread *curr = current();
+ struct thread *st, *nt, *best;
+
+#if DEBUG
+ if (__unlikely(irq_state() & 0x200)) {
+ dprintf("In __schedule with interrupts on!\n");
+ kaboom();
+ }
+#endif
+
+ /*
+ * Are we called from inside sched_hook_func()? If so we'll
+ * schedule anyway on the way out.
+ */
+ if (in_sched_hook)
+ return;
+
+ dprintf("Schedule ");
+
+ /* Possibly update the information on which we make
+ * scheduling decisions.
+ */
+ if (sched_hook_func) {
+ in_sched_hook = true;
+ sched_hook_func();
+ in_sched_hook = false;
+ }
+
+ /*
+ * The unusual form of this walk is because we have to start with
+ * the thread *following* curr, and curr may not actually be part
+ * of the list anymore (in the case of __exit_thread).
+ */
+ best = NULL;
+ nt = st = container_of(curr->list.next, struct thread, list);
+ do {
+ if (__unlikely(nt->thread_magic != THREAD_MAGIC)) {
+ dprintf("Invalid thread on thread list %p magic = 0x%08x\n",
+ nt, nt->thread_magic);
+ kaboom();
+ }
+
+ dprintf("Thread %p (%s) ", nt, nt->name);
+ if (!nt->blocked) {
+ dprintf("runnable priority %d\n", nt->prio);
+ if (!best || nt->prio < best->prio)
+ best = nt;
+ } else {
+ dprintf("blocked\n");
+ }
+ nt = container_of(nt->list.next, struct thread, list);
+ } while (nt != st);
+
+ if (!best)
+ kaboom(); /* No runnable thread */
+
+ if (best != curr) {
+ uint64_t tsc;
+
+ asm volatile("rdtsc" : "=A" (tsc));
+
+ dprintf("@ %llu -> %p (%s)\n", tsc, best, best->name);
+ __switch_to(best);
+ } else {
+ dprintf("no change\n");
+ }
+}
+
+/*
+ * This can be called from "normal" code...
+ */
+void thread_yield(void)
+{
+ irq_state_t irq = irq_save();
+ __schedule();
+ irq_restore(irq);
+}
diff --git a/core/thread/sem_asm.S b/core/thread/sem_asm.S
new file mode 100644
index 00000000..ce67471a
--- /dev/null
+++ b/core/thread/sem_asm.S
@@ -0,0 +1,16 @@
+ .globl sem_down
+ .type sem_down, @function
+sem_down:
+ decl (%eax)
+ js __sem_down_slow
+ xorl %eax, %eax
+ ret
+ .size sem_down, .-sem_down
+
+ .globl sem_up
+ .type sem_up, @function
+sem_up:
+ incl (%eax)
+ jle __sem_up_slow
+ ret
+ .size sem_up, .-sem_up
diff --git a/core/thread/semaphore.c b/core/thread/semaphore.c
new file mode 100644
index 00000000..c99af9c5
--- /dev/null
+++ b/core/thread/semaphore.c
@@ -0,0 +1,87 @@
+#include <sys/cpu.h>
+#include "thread.h"
+
+void sem_init(struct semaphore *sem, int count)
+{
+ if (!!sem) {
+ sem->list.next = sem->list.prev = &sem->list;
+ sem->count = count;
+ }
+}
+
+mstime_t __sem_down_slow(struct semaphore *sem, mstime_t timeout)
+{
+ irq_state_t irq;
+ mstime_t rv;
+
+ irq = irq_save();
+
+ if (!sem_is_valid(sem)) {
+ rv = -1;
+ } else if (sem->count >= 0) {
+ /* Something already freed the semaphore on us */
+ rv = 0;
+ } else if (timeout == -1) {
+ /* Immediate timeout */
+ sem->count++;
+ rv = -1;
+ } else {
+ /* Put the thread to sleep... */
+
+ struct thread_block block;
+ struct thread *curr = current();
+ mstime_t now = ms_timer();
+
+ block.thread = curr;
+ block.semaphore = sem;
+ block.block_time = now;
+ block.timeout = timeout ? now+timeout : 0;
+ block.timed_out = false;
+
+ curr->blocked = &block;
+
+ /* Add to the end of the wakeup list */
+ block.list.prev = sem->list.prev;
+ block.list.next = &sem->list;
+ sem->list.prev = &block.list;
+ block.list.prev->next = &block.list;
+
+ __schedule();
+
+ rv = block.timed_out ? -1 : ms_timer() - block.block_time;
+ }
+
+ irq_restore(irq);
+ return rv;
+}
+
+void __sem_up_slow(struct semaphore *sem)
+{
+ irq_state_t irq;
+ struct thread_list *l;
+
+ irq = irq_save();
+
+ /*
+ * It's possible that something did a down on the semaphore, but
+ * didn't get to add themselves to the queue just yet. In that case
+ * we don't have to do anything, since the bailout clause in
+ * __sem_down_slow will take care of it.
+ */
+ if (!!sem) {
+ l = sem->list.next;
+ if (l != &sem->list) {
+ struct thread_block *block;
+ block = container_of(l, struct thread_block, list);
+
+ sem->list.next = block->list.next;
+ block->list.next->prev = &sem->list;
+
+ block->thread->blocked = NULL;
+
+ __schedule();
+ }
+ }
+
+ irq_restore(irq);
+}
diff --git a/core/thread/start_thread.c b/core/thread/start_thread.c
new file mode 100644
index 00000000..2e4320a4
--- /dev/null
+++ b/core/thread/start_thread.c
@@ -0,0 +1,69 @@
+#include <string.h>
+#include <stdlib.h>
+#include <com32.h>
+#include "core.h"
+#include "thread.h"
+
+#define REAL_MODE_STACK_SIZE 4096
+#define MIN_STACK_SIZE 16384
+#define THREAD_ALIGN 64 /* Thread alignment */
+
+extern void __start_thread(void);
+
+struct thread *start_thread(const char *name, size_t stack_size, int prio,
+ void (*start_func)(void *), void *func_arg)
+{
+ irq_state_t irq;
+ struct thread *curr, *t;
+ char *stack, *rmstack;
+ const size_t thread_mask = THREAD_ALIGN - 1;
+ struct thread_stack *sp;
+
+ if (stack_size < MIN_STACK_SIZE)
+ stack_size = MIN_STACK_SIZE;
+
+ stack_size = (stack_size + thread_mask) & ~thread_mask;
+ stack = malloc(stack_size + sizeof(struct thread));
+ if (!stack)
+ return NULL;
+ rmstack = lmalloc(REAL_MODE_STACK_SIZE);
+ if (!rmstack) {
+ free(stack);
+ return NULL;
+ }
+
+ t = (struct thread *)stack;
+ memset(t, 0, sizeof *t);
+ t->stack = stack;
+ t->rmstack = rmstack;
+ stack += sizeof(struct thread);
+
+ /* sp allocated from the end of the stack */
+ sp = (struct thread_stack *)(stack + stack_size) - 1;
+ t->esp = sp;
+
+ sp->errno = 0;
+ sp->rmss = SEG(rmstack);
+ sp->rmsp = REAL_MODE_STACK_SIZE;
+ sp->esi = (size_t)start_func;
+ sp->edi = (size_t)func_arg;
+ sp->ebx = irq_state(); /* Inherit the IRQ state from the spawner */
+ sp->eip = __start_thread;
+ t->prio = prio;
+ t->name = name;
+
+ irq = irq_save();
+ curr = current();
+
+ t->thread_magic = THREAD_MAGIC;
+
+ t->list.prev = &curr->list;
+ t->list.next = curr->list.next;
+ curr->list.next = &t->list;
+ t->list.next->prev = &t->list;
+
+ __schedule();
+
+ irq_restore(irq);
+ return t;
+}
diff --git a/core/thread/thread_asm.S b/core/thread/thread_asm.S
new file mode 100644
index 00000000..ec3e0add
--- /dev/null
+++ b/core/thread/thread_asm.S
@@ -0,0 +1,37 @@
+ .globl __switch_to
+ .type __switch_to, @function
+__switch_to:
+ movl __current, %edx
+ pushl %ebx
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+ pushl RealModeSSSP
+ pushl errno /* Hack! */
+ movl %esp, (%edx)
+
+ movl %eax, __current
+ movl (%eax), %esp
+ popl errno
+ popl RealModeSSSP
+ popl %edi
+ popl %esi
+ popl %ebp
+ popl %ebx
+ ret
+ .size __switch_to, .-__switch_to
+
+ .globl __start_thread
+ .type __start_thread, @function
+__start_thread:
+ movl %edi, %eax /* Thread function argument */
+
+ pushl $0 /* For gdb's benefit */
+ movl %esp, %ebp /* For gdb's benefit */
+
+ pushl %ebx /* Set up the flags/interrupt state */
+ popfl
+
+ call *%esi /* Run the desired function */
+ jmp __exit_thread /* If we get here, kill the thread */
+ .size __start_thread, .-__start_thread
diff --git a/core/thread/timeout.c b/core/thread/timeout.c
new file mode 100644
index 00000000..409ad6d7
--- /dev/null
+++ b/core/thread/timeout.c
@@ -0,0 +1,41 @@
+/*
+ * timeout.c
+ *
+ */
+
+#include "thread.h"
+
+/*
+ * __thread_process_timeouts()
+ *
+ * Look for threads that have timed out. This should be called
+ * under interrupt lock, before calling __schedule().
+ */
+void __thread_process_timeouts(void)
+{
+ struct thread *curr = current();
+ struct thread_list *tp;
+ struct thread *t;
+ mstime_t now = ms_timer();
+ struct thread_block *block;
+ mstime_t timeout;
+
+ /* The current thread is obviously running, so no need to check... */
+ for (tp = curr->list.next; tp != &curr->list; tp = tp->next) {
+ t = container_of(tp, struct thread, list);
+ if ((block = t->blocked) && (timeout = block->timeout)) {
+ if ((mstimediff_t)(timeout - now) <= 0) {
+ struct semaphore *sem = block->semaphore;
+ /* Remove us from the queue and increase the count */
+ block->list.next->prev = block->list.prev;
+ block->list.prev->next = block->list.next;
+ sem->count++;
+
+ t->blocked = NULL;
+ block->timed_out = true;
+
+ __schedule(); /* Normally sets just __need_schedule */
+ }
+ }
+ }
+}
diff --git a/core/timer.inc b/core/timer.inc
index b01ff917..64f81a72 100644
--- a/core/timer.inc
+++ b/core/timer.inc
@@ -32,6 +32,7 @@ timer_init:
mov dword [BIOS_timer_hook],timer_irq
ret
+ global timer_cleanup:function hidden
timer_cleanup:
; Unhook INT 1Ch
mov eax,[BIOS_timer_next]
@@ -42,16 +43,18 @@ timer_cleanup:
; The specified frequency is 14.31818 MHz/12/65536; this turns out
; to be a period of 54.92542 ms, or 0x36.ece8(187c) hexadecimal.
;
+ global timer_irq:function hidden
timer_irq:
inc dword [cs:__jiffies]
add word [cs:__ms_timer_adj],0xece8
adc dword [cs:__ms_timer],0x36
jmp 0:0
+ global BIOS_timer_next:data hidden
BIOS_timer_next equ $-4
section .data16
alignz 4
- global __jiffies, __ms_timer
+ global __jiffies:data hidden, __ms_timer
__jiffies dd 0 ; Clock tick timer
__ms_timer dd 0 ; Millisecond timer
__ms_timer_adj dw 0 ; Millisecond timer correction factor
diff --git a/core/ui.inc b/core/ui.inc
deleted file mode 100644
index 631860f8..00000000
--- a/core/ui.inc
+++ /dev/null
@@ -1,743 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;
-; This file should be entered with the config file open (for getc)
-;
-load_config_file:
- call parse_config ; Parse configuration file
-no_config_file:
-
- call adv_init
-;
-; Check for an ADV boot-once entry
-;
- mov dl,ADV_BOOTONCE
- call adv_get
- jcxz .no_bootonce
-
-.have_bootone:
- ; We apparently have a boot-once set; clear it and
- ; then execute the boot-once...
-
- ; Save the boot-once data; SI = data, CX = length
- mov di,command_line
- rep movsb
- xor ax,ax
- stosb
-
- ; Clear the boot-once data from the ADV
- xor cx,cx ; Set to zero = delete
- call adv_set
- jc .err
- call adv_write
-.err: jmp load_kernel
-
-.no_bootonce:
-
-;
-; Check whether or not we are supposed to display the boot prompt.
-;
-check_for_key:
- test byte [KbdFlags],5Bh ; Shift Alt Caps Scroll
- jnz enter_command
- cmp word [ForcePrompt],0 ; Force prompt?
- jz auto_boot
- cmp word [DefaultLevel],1 ; Active UI statement?
- ja auto_boot
-
-enter_command:
- cmp word [NoEscape],0 ; If NOESCAPE, no prompt,
- jne auto_boot ; always run default cmd
-
- mov si,boot_prompt
- call writestr
-
- mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
- mov di,command_line
-
-;
-; get the very first character -- we can either time
-; out, or receive a character press at this time. Some dorky BIOSes stuff
-; a return in the buffer on bootup, so wipe the keyboard buffer first.
-;
-clear_buffer: mov ah,11h ; Check for pending char
- int 16h
- jz get_char_time
- mov ah,10h ; Get char
- int 16h
- jmp short clear_buffer
-
- ; For the first character, both KbdTimeout and
- ; TotalTimeout apply; after that, only TotalTimeout.
-
-get_char_time:
- mov eax,[TotalTimeout]
- mov [ThisTotalTo],eax
- mov eax,[KbdTimeout]
- mov [ThisKbdTo],eax
-
-get_char:
- call getchar_timeout
- and dword [ThisKbdTo],0 ; For the next time...
-
- and al,al
- jz func_key
-
-got_ascii: cmp al,7Fh ; <DEL> == <BS>
- je backspace
- cmp al,' ' ; ASCII?
- jb not_ascii
- ja enter_char
- cmp di,command_line ; Space must not be first
- je short get_char
-enter_char: test byte [FuncFlag],1
- jnz ctrl_f ; Keystroke after <Ctrl-F>
- cmp di,max_cmd_len+command_line ; Check there's space
- jnb short get_char
- stosb ; Save it
- call writechr ; Echo to screen
- jmp short get_char
-not_ascii:
- cmp al,0Dh ; Enter
- je command_done
- cmp al,09h ; Tab
- je display_labels
- cmp al,'F' & 1Fh ; <Ctrl-F>
- je set_func_flag
-%if IS_PXELINUX
- cmp al,'N' & 1Fh ; <Ctrl-N>
- je show_network_info
-%endif
- cmp al,'U' & 1Fh ; <Ctrl-U>
- je kill_command ; Kill input line
- cmp al,'V' & 1Fh ; <Ctrl-V>
- je print_version
- cmp al,'X' & 1Fh ; <Ctrl-X>
- je force_text_mode
- cmp al,08h ; Backspace
- jne get_char
-backspace: cmp di,command_line ; Make sure there is anything
- je get_char ; to erase
- dec di ; Unstore one character
- mov si,wipe_char ; and erase it from the screen
- call writestr
-get_char_2:
- jmp short get_char
-
-kill_command:
- call crlf
- jmp enter_command
-
-force_text_mode:
- call vgaclearmode
- jmp enter_command
-
-set_func_flag:
- mov byte [FuncFlag],1
- jmp short get_char_2
-
-display_labels:
- cmp word [NoComplete],0 ; Label completion enabled?
- jne get_char_2
- push di ; Save pointer
- mov cx,di
- sub cx,command_line
- call crlf
- mov esi,[HighMemSize] ; Start from top of memory
-.scan:
- cmp esi,[VKernelEnd]
- jbe .not_vk
-
- push cx ; save command line size
-
- mov edi,VKernelBuf
- pm_call rllunpack
- ; ESI updated on return
-
- sub di,cx ; Return to beginning of buf
- pop cx ; restore command line size
- push si ; save SI
- cmp cx,0
- jz .print
- push di
- push cx
- mov si,command_line
- es repe cmpsb
- pop cx
- pop di
- jne .next
-.print:
- mov al,' '
- call writechr
-
- mov si,di
- call writestr
-.next:
- pop si ; restore SI
- jmp .scan
-.not_vk:
- call crlf
- jmp fk_wrcmd
-
-ctrl_f:
- xor ah,ah
- mov [FuncFlag],ah
- cmp al,'0'
- jb get_char_2
- je .zero ; <Ctrl-F>0 = F10
- or al,20h ; Lower case
- cmp al,'9'
- jna .digit
- cmp al,'a' ; F10-F12 = <Ctrl-F>A, B, C
- jb get_char_2
- cmp al,'c'
- ja get_char_2
- sub al,'a'-10
- jmp show_help
-.zero:
- mov al,10
- jmp show_help
-.digit:
- sub al,'1'
- jmp show_help
-
-func_key:
- ; AL = 0 if we get here
- xchg al,ah
- cmp al,44h ; F10
- ja .f11_f12
- sub al,3Bh ; F1
- jb get_char_2
- jmp show_help
-.f11_f12:
- cmp al,85h ; F11
- jb get_char_2
- cmp al,86h ; F12
- ja get_char_2
- sub al,85h-10
-
-show_help: ; AX = func key # (0 = F1, 9 = F10, 11 = F12)
- push di ; Save end-of-cmdline pointer
- shl ax,FILENAME_MAX_LG2 ; Convert to pointer
- add ax,FKeyName
- xchg di,ax
- cmp byte [di+NULLOFFSET],NULLFILE
- je short fk_nofile ; Undefined F-key
- call core_open
- jz short fk_nofile ; File not found
- call crlf
- call get_msg_file
- jmp short fk_wrcmd
-
-print_version:
- push di ; Command line write pointer
- mov si,syslinux_banner
- call writestr
-%ifdef HAVE_BIOSNAME
- mov si,[BIOSName]
- call writestr
-%endif
- mov si,copyright_str
- call writestr
-
- ; ... fall through ...
-
- ; Write the boot prompt and command line again and
- ; wait for input. Note that this expects the cursor
- ; to already have been CRLF'd, and that the old value
- ; of DI (the command line write pointer) is on the stack.
-fk_wrcmd:
- mov si,boot_prompt
- call writestr
- pop di ; Command line write pointer
- push di
- mov byte [di],0 ; Null-terminate command line
- mov si,command_line
- call writestr ; Write command line so far
-fk_nofile: pop di
- jmp get_char
-
-;
-; Show network info (in the form of the ipappend strings)
-;
-%if IS_PXELINUX
-show_network_info:
- push di ; Command line write pointer
- call crlf
- mov si,IPAppends ; See comboot.doc
- mov cx,numIPAppends
-.loop:
- lodsw
- push si
- mov si,ax
- call writestr
- call crlf
- pop si
- loop .loop
- jmp fk_wrcmd
-%endif
-
-;
-; Jump here to run the default command line
-;
-auto_boot:
- cmp word [DefaultLevel],0 ; No UI or DEFAULT?
- jne .have_default
- mov si,no_default_msg
- call writestr
- cmp word [NoEscape],0 ; NOESCAPE but no DEFAULT?
- jne kaboom ; If so, we're stuck!
- jmp enter_command
-
-.have_default:
- mov si,default_cmd
- mov di,command_line
- mov cx,(max_cmd_len+4) >> 2
- rep movsd
- jmp short load_kernel
-
- section .data16
-no_default_msg db 'No DEFAULT or UI configuration directive found!'
- db CR, LF, 0
-
- section .text16
-
-;
-; Jump here when the command line is completed
-;
-command_done:
- call crlf
- cmp di,command_line ; Did we just hit return?
- je auto_boot
- xor al,al ; Store a final null
- stosb
-
-load_kernel: ; Load the kernel now
-;
-; First we need to mangle the kernel name the way DOS would...
-;
- mov si,command_line
- mov di,KernelName
- push si
- pm_call pm_mangle_name
- pop si
-;
-; Fast-forward to first option (we start over from the beginning, since
-; pm_mangle_name doesn't necessarily return a consistent ending state.)
-;
-clin_non_wsp: lodsb
- cmp al,' '
- ja clin_non_wsp
-clin_is_wsp: and al,al
- jz clin_opt_ptr
- lodsb
- cmp al,' '
- jbe clin_is_wsp
-clin_opt_ptr: dec si ; Point to first nonblank
- mov [CmdOptPtr],si ; Save ptr to first option
-;
-; If "allowoptions 0", put a null character here in order to ignore any
-; user-specified options.
-;
- mov ax,[AllowOptions]
- and ax,ax
- jnz clin_opt_ok
- mov [si],al
-clin_opt_ok:
-
-;
-; Now check if it is a "virtual kernel"
-;
-vk_check:
- mov esi,[HighMemSize] ; Start from top of memory
-.scan:
- cmp esi,[VKernelEnd]
- jbe .not_vk
-
- mov edi,VKernelBuf
- pm_call rllunpack
- ; ESI updated on return
-
- sub di,cx ; Return to beginning of buf
- push si
- mov si,command_line
-.loop:
- lodsb
- cmp al,' '
- jbe .done
- scasb
- je .loop
-.nomatch:
- pop si
- jmp .scan
-.done:
- cmp byte [di],0 ; Must match end of string
- jne .nomatch
- pop si
-
-;
-; We *are* using a "virtual kernel"
-;
-.found:
- push es
- push word real_mode_seg
- pop es
- mov di,cmd_line_here
- mov si,VKernelBuf+vk_append
- mov cx,[VKernelBuf+vk_appendlen]
- rep movsb
- mov byte [es:di],cl ; Null-terminate
- mov [CmdLinePtr],di ; Where to add rest of cmd
- pop es
- mov di,KernelName
- push di
- mov si,VKernelBuf+vk_rname
- mov cx,FILENAME_MAX ; We need ECX == CX later
- rep movsb
- pop di
-%if IS_PXELINUX
- mov al,[VKernelBuf+vk_ipappend]
- mov [IPAppend],al
-%endif
- xor bx,bx ; Try only one version
-
- mov al,[VKernelBuf+vk_type]
- mov [KernelType],al
-
- ; Is this a "localboot" pseudo-kernel?
- cmp al,VK_LOCALBOOT ; al == KernelType
- mov ax,[VKernelBuf+vk_rname] ; Possible localboot type
- je local_boot
- jmp get_kernel
-
-.not_vk:
-;
-; Not a "virtual kernel" - check that's OK and construct the command line
-;
- cmp word [AllowImplicit],byte 0
- je bad_implicit
- push es
- push si
- push di
- mov di,real_mode_seg
- mov es,di
- mov si,AppendBuf
- mov di,cmd_line_here
- mov cx,[AppendLen]
- rep movsb
- mov byte [es:di],cl ; Null-terminate
- mov [CmdLinePtr],di
- pop di
- pop si
- pop es
-
- mov [KernelType], cl ; CL == 0 here
-
-;
-; Find the kernel on disk
-;
-get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/extension
- mov di,KernelName
- cmp byte [di],' '
- jbe bad_kernel ; Missing kernel name
- xor al,al
- mov cx,FILENAME_MAX-5 ; Need 4 chars + null
- repne scasb ; Scan for final null
- jne .no_skip
- dec di ; Point to final null
-.no_skip: mov [KernelExtPtr],di
- mov bx,exten_table
-.search_loop: push bx
- mov di,KernelName ; Search on disk
- pm_call pm_searchdir
- pop bx
- jnz kernel_good
- mov eax,[bx] ; Try a different extension
- mov si,[KernelExtPtr]
- mov [si],eax
- mov byte [si+4],0
- add bx,byte 4
- cmp bx,exten_table_end
- jna .search_loop ; allow == case (final case)
- ; Fall into bad_kernel
-;
-; bad_kernel: Kernel image not found
-; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
-;
-bad_implicit:
-bad_kernel:
- mov cx,[OnerrorLen]
- and cx,cx
- jnz on_error
-.really:
- mov si,err_notfound ; Complain about missing kernel
- call writestr
- mov si,KernelName
- call writestr
- mov si,crlf_msg
- jmp abort_load ; Ask user for clue
-
-;
-; on_error: bad kernel, but we have onerror set; CX = OnerrorLen
-;
-on_error:
- mov si,Onerror
- mov di,command_line
- push si ; <A>
- push di ; <B>
- push cx ; <C>
- push cx ; <D>
- push di ; <E>
- repe cmpsb
- pop di ; <E> di == command_line
- pop bx ; <D> bx == [OnerrorLen]
- je bad_kernel.really ; Onerror matches command_line already
- neg bx ; bx == -[OnerrorLen]
- lea cx,[max_cmd_len+bx]
- ; CX == max_cmd_len-[OnerrorLen]
- mov di,command_line+max_cmd_len-1
- mov byte [di+1],0 ; Enforce null-termination
- lea si,[di+bx]
- std
- rep movsb ; Make space in command_line
- cld
- pop cx ; <C> cx == [OnerrorLen]
- pop di ; <B> di == command_line
- pop si ; <A> si == Onerror
- rep movsb
- jmp load_kernel
-
-;
-; kernel_corrupt: Called if the kernel file does not seem healthy
-;
-kernel_corrupt: mov si,err_notkernel
- jmp abort_load
-
-;
-; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts
-; which can be adjusted by the caller based on the corresponding
-; master variables; on return they're updated.
-;
-; This cheats. If we say "no timeout" we actually get a timeout of
-; 7.5 years.
-;
-getchar_timeout:
- call vgashowcursor
- call reset_idle
-
-.loop:
- push word [__jiffies]
- call pollchar
- jnz .got_char
- call do_idle
- pop ax
- cmp ax,[__jiffies] ; Has the timer advanced?
- je .loop
-
- dec dword [ThisKbdTo]
- jz .timeout
- dec dword [ThisTotalTo]
- jnz .loop
-
-.timeout:
- ; Timeout!!!!
- pop cx ; Discard return address
- call vgahidecursor
- mov si,Ontimeout ; Copy ontimeout command
- mov di,command_line
- mov cx,[OntimeoutLen] ; if we have one...
- rep movsb
- jmp command_done
-
-.got_char:
- pop cx ; Discard
- call getchar
- call vgahidecursor
- ret
-
-;
-; This is it! We have a name (and location on the disk)... let's load
-; that sucker!! First we have to decide what kind of file this is; base
-; that decision on the file extension. The following extensions are
-; recognized; case insensitive:
-;
-; .com - COMBOOT image
-; .cbt - COMBOOT image
-; .c32 - COM32 image
-; .bs - Boot sector
-; .0 - PXE bootstrap program (PXELINUX only)
-; .bin - Boot sector
-; .bss - Boot sector, but transfer over DOS superblock (SYSLINUX only)
-; .img - Floppy image (ISOLINUX only)
-;
-; Anything else is assumed to be a Linux kernel.
-;
- section .bss16
- alignb 4
-Kernel_EAX resd 1
-Kernel_SI resw 1
-
- section .text16
-kernel_good_saved:
- ; Alternate entry point for which the return from
- ; searchdir is stored in memory. This is used for
- ; COMBOOT function INT 22h, AX=0016h.
- mov si,[Kernel_SI]
- mov eax,[Kernel_EAX]
-
-kernel_good:
- pushad
- ;
- ; Common initialization for all kernel types
- ;
- xor ax,ax
- mov [InitRDPtr],ax
- mov [QuietBoot],al
-%if IS_PXELINUX
- mov [KeepPXE],al
-%endif
-
- ; Default memory limit, can be overridden by image loaders
- mov eax,[HighMemRsvd]
- mov [MyHighMemSize],eax
-
- popad
-
- push di
- push ax
- mov di,KernelName
- xor al,al
- mov cx,FILENAME_MAX
- repne scasb
- jne .one_step
- dec di
-.one_step: mov ecx,[di-4] ; 4 bytes before end
- pop ax
- pop di
-
-;
-; At this point, EAX contains the size of the kernel, SI contains
-; the file handle/cluster pointer, and ECX contains the extension (if any.)
-;
- movzx di,byte [KernelType]
- add di,di
- jmp [kerneltype_table+di]
-
-is_unknown_filetype:
- or ecx,20202000h ; Force lower case (except dot)
-
- cmp ecx,'.com'
- je is_comboot_image
- cmp ecx,'.cbt'
- je is_comboot_image
- cmp ecx,'.c32'
- je is_com32_image
-%if IS_ISOLINUX
- cmp ecx,'.img'
- je is_disk_image
-%endif
- cmp ecx,'.bss'
- je is_bss_sector
- cmp ecx,'.bin'
- je is_bootsector
- shr ecx,8
- cmp ecx,'.bs'
- je is_bootsector
- shr ecx,8
- cmp cx,'.0'
- je is_bootsector
-
- ; Otherwise Linux kernel
- jmp is_linux_kernel
-
-is_config_file:
- push si
- call make_plain_cmdline
- pm_call pm_is_config_file
- pop si
- call openfd
- call reset_config
- jmp load_config_file
-
-; This is an image type we can't deal with
-is_bad_image:
- mov si,err_badimage
- call writestr
- jmp enter_command
-
-%if IS_SYSLINUX
- ; ok
-%else
-is_bss_sector equ is_bad_image
-%endif
-
-is_disk_image equ is_bad_image ; No longer supported
-
- section .data16
-boot_prompt db 'boot: ', 0
-wipe_char db BS, ' ', BS, 0
-err_badimage db 'Invalid image type for this media type!', CR, LF, 0
-err_notfound db 'Could not find kernel image: ',0
-err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
-
-
- alignz 2
-kerneltype_table:
- dw is_unknown_filetype ; VK_KERNEL
- dw is_linux_kernel ; VK_LINUX
- dw is_bootsector ; VK_BOOT
- dw is_bss_sector ; VK_BSS
- dw is_bootsector ; VK_PXE
- dw is_disk_image ; VK_FDIMAGE
- dw is_comboot_image ; VK_COMBOOT
- dw is_com32_image ; VK_COM32
- dw is_config_file ; VK_CONFIG
-
- section .bss16
- alignb 4
-ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
-ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
-KernelExtPtr resw 1 ; During search, final null pointer
-CmdOptPtr resw 1 ; Pointer to first option on cmd line
-KbdFlags resb 1 ; Check for keyboard escapes
-FuncFlag resb 1 ; Escape sequences received from keyboard
-KernelType resb 1 ; Kernel type, from vkernel, if known
-
- section .text16
-;
-; Linux kernel loading code is common.
-;
-%include "runkernel.inc"
-
-;
-; COMBOOT-loading code
-;
-%include "comboot.inc"
-%include "com32.inc"
-%include "cmdline.inc"
-
-;
-; Boot sector loading code
-;
-%include "bootsect.inc"
-
-;
-; Abort loading code
-;
-%include "abort.inc"
-
-;
-; Hardware cleanup common code
-;
-%include "cleanup.inc"
diff --git a/core/writehex.c b/core/writehex.c
new file mode 100644
index 00000000..fde27037
--- /dev/null
+++ b/core/writehex.c
@@ -0,0 +1,70 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ */
+#include <core.h>
+
+/*
+ * writehex.c
+ *
+ * Write hexadecimal numbers to the console
+ *
+ */
+
+static inline void __writehex(uint32_t h, int digits)
+{
+ while (digits) {
+ uint8_t shift;
+ uint8_t al;
+
+ shift = --digits;
+ al = ((h & 0x0f << shift) >> shift);
+ if (al < 10)
+ al += '0';
+ else
+ al += 'A' - 10;
+
+ writechr(al);
+ }
+}
+
+/*
+ * writehex[248]: Write a hex number in (AL, AX, EAX) to the console
+ */
+void writehex2(uint32_t h)
+{
+ __writehex(h, 2);
+}
+
+void writehex4(uint8_t h)
+{
+ __writehex(h, 4);
+}
+
+void writehex8(uint8_t h)
+{
+ __writehex(h, 8);
+}
+
+void pm_writehex2(com32sys_t *regs)
+{
+ writehex2(regs->eax.b[0]);
+}
+
+void pm_writehex4(com32sys_t *regs)
+{
+ writehex4(regs->eax.w[0]);
+}
+
+void pm_writehex8(com32sys_t *regs)
+{
+ writehex8(regs->eax.l);
+}
diff --git a/core/writestr.c b/core/writestr.c
new file mode 100644
index 00000000..fb9de348
--- /dev/null
+++ b/core/writestr.c
@@ -0,0 +1,47 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * -----------------------------------------------------------------------
+ *
+ *
+ * writestr.c
+ *
+ * Code to write a simple string.
+ */
+#include <com32.h>
+#include <core.h>
+
+/*
+ * crlf: Print a newline
+ */
+void crlf(void)
+{
+ writechr('\r');
+ writechr('\n');
+}
+
+/*
+ * writestr: write a null-terminated string to the console, saving
+ * registers on entry.
+ *
+ * Note: writestr_early and writestr are distinct in
+ * SYSLINUX and EXTLINUX, but not PXELINUX and ISOLINUX
+ */
+void writestr(char *str)
+{
+ while (*str)
+ writechr(*str++);
+}
+
+void pm_writestr(com32sys_t *regs)
+{
+ writestr(MK_PTR(regs->ds, regs->esi.w[0]));
+}
diff --git a/core/writestr.inc b/core/writestr.inc
deleted file mode 100644
index 9c11b320..00000000
--- a/core/writestr.inc
+++ /dev/null
@@ -1,47 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; writestr.inc
-;;
-;; Code to write a simple string.
-;;
-
-;
-; crlf: Print a newline
-;
-crlf: push ax
- mov al,CR
- call writechr
- mov al,LF
- call writechr
- pop ax
- ret
-
-;
-; writestr: write a null-terminated string to the console, saving
-; registers on entry.
-;
-; Note: writestr_early and writestr are distinct in
-; SYSLINUX and EXTLINUX, but not PXELINUX and ISOLINUX
-;
-writestr:
- pushfd
- pushad
-.top: lodsb
- and al,al
- jz .end
- call writechr
- jmp short .top
-.end: popad
- popfd
- ret
diff --git a/diag/geodsp/Makefile b/diag/geodsp/Makefile
index f97a1bd5..67637f91 100644
--- a/diag/geodsp/Makefile
+++ b/diag/geodsp/Makefile
@@ -26,7 +26,7 @@ coredir = $(topdir)/core
BTARGET = geodsp1s.bin geodspms.bin \
geodsp1s.img.xz geodspms.img.xz
-NASMOPT = -i $(coredir)/ -Ox -f bin
+NASMOPT = -i $(coredir)/ -Ox -f bin -dBINFMT
NASMOPT += -w+orphan-labels
CFLAGS = -g -O
diff --git a/diag/mbr/README b/diag/mbr/README
index 96b67c6c..786816c6 100644
--- a/diag/mbr/README
+++ b/diag/mbr/README
@@ -11,7 +11,7 @@ Writing out an MBR is straight forward (it is assumed below that /dev/hda is the
dd conv=notrunc bs=440 count=1 if=mbr.bin of=/dev/hda
-Writing a VBR to match Syslinux requires more work as it must have a jump and be offset into the partition (and as a result the code must be compaible with this offset):
+Writing a VBR to match Syslinux requires more work as it must have a jump and be offset into the partition (and as a result the code must be compatible with this offset):
echo -en "\0353\0130\0220" |dd conv=notrunc bs=1 count=3 of=/dev/hda1
dd conv=notrunc bs=2 count=210 seek=45 if=mbr.bin of=/dev/hda1
diff --git a/doc/chain.txt b/doc/chain.txt
index 6dd0632d..2321c102 100644
--- a/doc/chain.txt
+++ b/doc/chain.txt
@@ -53,29 +53,27 @@ Module invocation:
chain [drive/partition] [options]
+In case of repeated arguments, rightmost ones take precedence.
+
+
DRIVE / PARTITION SPECIFICATION
Drive can be specified as 'hd#', 'fd#', 'boot', 'mbr', or 'guid'.
-- 'mbr' will select a drive by a signature.
-- 'guid' will select a drive by a guid
+- 'mbr' will select a drive by its signature.
+- 'guid' will select a drive by its guid (GPT only).
- 'boot' is the drive syslinux was booted from. This is the default value, if
nothing else is specified.
- 'hd#' and 'fd#' are standard ways to specify drive number as seen by bios,
starting from 0.
-Option 'guid' is shared with partition selection (see below). If you happened
+Option 'guid' is shared with partition selection (see below). If you happen
to have non-unique guids, they are searched in disk0, partitions of disk0,
disk1 ... order.
-The priority of those options are the same as in the above list.
-
-If you specify the same value more than once, the last value will be used.
-
'mbr' and 'guid' take extra parameter - you should use ':' or '=' as a
delimiter.
-
Partition can be specified as '#', 'guid', 'label' or 'fs'.
- 'guid' option will select a partition by a guid (not a type guid !)
@@ -85,11 +83,10 @@ Partition can be specified as '#', 'guid', 'label' or 'fs'.
- '#' is the standard method. Partitions 1-4 are primary, 5+ logical, 0 = boot
MBR (default).
-The priority of those options are the same as in the above list.
-
If you use a number to select a partition it should be specified after a drive
using space or comma as delimiters (after 'hd#', 'fd#', 'mbr', 'guid' or 'boot').
+
OPTIONS
file=<file>
*nofile
@@ -110,11 +107,11 @@ This triplet lets you alter the addresses a file will use. It's loaded at
other bootloader or kernel, it's almost always mandatory.
The defaults, if option is not specified, are 0:0x7c00:0x7c00
-If any of the fields are ommited (e.g. 0x2000::), they default to 0.
+If any of the fields are omitted (e.g. 0x2000::), they default to 0.
sect=<segment>:<offset>:<ip>
- nosect
*sect=0:0x7c00:0x7c00
+ nosect
nosect sets: nomaps
This triplet lets you alter the addresses a sector will use. It's loaded at
@@ -126,7 +123,7 @@ expect relocated sector at some particular address (e.g. DRKM).
is being chainloaded, sector is not necessary.
The defaults if option is not specified, are 0:0x7c00:0x7c00.
-If some of the fields are ommited (e.g. 0x2000::), they default to 0.
+If some of the fields are omitted (e.g. 0x2000::), they default to 0.
*maps
nomaps
@@ -229,8 +226,24 @@ stacks in memory (pxelinux only).
This option will wait for a keypress right before continuing the chainloading.
Useful to see warnings emited by the chain module.
- *nobreak
+ prefmbr
+ *noprefmbr
+
+In the case of presence of non-standard hybrid MBR/GPT layout, this flag makes
+chain module prefer MBR layout over GPT.
+
+ relax
+ *norelax
+
+This option inhibits sanity checks during the traversal of the partition table.
+This is potentially useful in corner cases, when for example an usb stick moved
+to some different computer would report smaller size than previously with
+partitions spanning the whole space. Normally partition iterator would report
+an error and abort in such case. Another case scenario is disk corruption in
+some later EMBR partition.
+
break
+ *nobreak
break sets: nofile nomaps nohand
It is possible to trigger a "service-only" run - The chain module will do
diff --git a/doc/comboot.txt b/doc/comboot.txt
deleted file mode 100644
index 6e9d7ab5..00000000
--- a/doc/comboot.txt
+++ /dev/null
@@ -1,940 +0,0 @@
-
- COMBOOT and COM32 files
-
-
-Syslinux supports simple standalone programs, using a file format
-similar to DOS ".com" files. A 32-bit version, called COM32, is also
-provided. A simple API provides access to a limited set of filesystem
-and console functions.
-
-
- ++++ COMBOOT file format ++++
-
-A COMBOOT file is a raw binary file containing 16-bit code. It should
-be linked to run at offset 0x100, and contain no absolute segment
-references. It is run in 16-bit real mode.
-
-A COMBOOT image can be written to be compatible with MS-DOS. Such a
-file will usually have extension ".com". A COMBOOT file which is not
-compatible with MS-DOS will usually have extension ".cbt".
-
-Before running the program, Syslinux sets up the following fields in
-the Program Segment Prefix (PSP), a structure at offset 0 in the
-program segment:
-
- Offset Size Meaning
- 0 word Contains an INT 20h instruction
- 2 word Contains the paragraph (16-byte "segment" address) at
- the end of memory available to the program.
- 128 byte Length of the command line arguments, including the leading
- space but not including the final CR character.
- 129 127b Command line arguments, starting with a space and ending
- with a CR character (ASCII 13).
-
-The program is allowed to use memory between the PSP paragraph (which
-all the CS, DS, ES and SS registers point to at program start) and the
-paragraph value given at offset 2.
-
-On startup, SP is set up to point to the end of the 64K segment, at
-0xfffe. Under DOS it is possible for SP to contain a smaller
-value if memory is very tight; this is never the case under Syslinux.
-
-The program should make no assumptions about what segment address it
-will be loaded at; instead it should look at the segment registers on
-program startup. Both DOS and Syslinux will guarantee CS == DS == ES
-== SS on program start; the program should not assume anything about
-the values of FS or GS.
-
-To exit, a program can either execute a near RET (which will jump to
-offset 0 which contains an INT 20h instruction, terminating the
-program), or execute INT 20h or INT 21h AH=00h or INT 21h AH=4Ch.
-If compatiblity with Syslinux 1.xx is desired, use INT 20h.
-
-
- ++++ COM32R file format ++++
-
-A COM32R file is a raw binary file containing 32-bit code. It should
-be self-relocating, as it will be loaded by the Syslinux core at any
-4K aligned address. It will be run in flat-memory 32-bit protected
-mode. Under Syslinux, it will be run in CPL 0, however, since it may
-be possible to create a COM32 execution engine that would run under
-something like Linux DOSEMU, it is recommended that the code does not
-assume CPL 0 unless absolutely necessary.
-
-A COM32R program must start with the byte sequence B8 FE 4C CD 21 (mov
-eax,21cd4cfeh) as a magic number.
-
-The COM32R format replaces the earlier COM32 format, which was linked
-to a fixed address (0x101000).
-
-A COM32R file should have extension ".c32".
-
-On startup, CS will be set up as a flat 32-bit code segment, and DS ==
-ES == SS will be set up as the equivalent flat 32-bit data segment.
-FS and GS are reserved for future use and are currently initialized to
-zero. A COM32R image should not assume any particular values of
-segment selectors.
-
-ESP is set up at the end of available memory and also serves as
-notification to the program how much memory is available.
-
-The following arguments are passed to the program on the stack:
-
- Address Size Meaning
- [ESP] dword Return (termination) address
- [ESP+4] dword Number of additional arguments (currently 8)
- [ESP+8] dword Pointer to the command line arguments (null-terminated string)
- [ESP+12] dword Pointer to INT call helper function
- [ESP+16] dword Pointer to low memory bounce buffer
- [ESP+20] dword Size of low memory bounce buffer
- [ESP+24] dword Pointer to FAR call helper function (new in 2.05)
- [ESP+28] dword Pointer to CDECL helper function (new in 3.54)
- [ESP+32] dword Amount of memory controlled by the Syslinux core (new in 3.74)
- [ESP+36] dword Pointer to the filename of the com32 module (new in 3.86)
- [ESP+40] dword Pointer to protected-mode functions (new in 4.00)
-
-The libcom32 startup code loads this into a structure named __com32,
-defined in <com32.h>:
-
-extern struct com32_sys_args {
- uint32_t cs_sysargs;
- char *cs_cmdline;
- void __cdecl(*cs_intcall)(uint8_t, const com32sys_t *, com32sys_t *);
- void *cs_bounce;
- uint32_t cs_bounce_size;
- void __cdecl(*cs_farcall)(uint32_t, const com32sys_t *, com32sys_t *);
- int __cdecl(*cs_cfarcall)(uint32_t, const void *, uint32_t);
- uint32_t cs_memsize;
- const char *cs_name;
- const struct com32_pmapi *cs_pm;
-} __com32;
-
-The intcall helper function can be used to issue BIOS or Syslinux API
-calls, and takes the interrupt number as first argument. The second
-argument is a pointer to the input register definition, an instance of
-the following structure (available in <com32.h>):
-
-typedef union {
- uint32_t l;
- uint16_t w[2];
- uint8_t b[4];
-} reg32_t;
-
-typedef struct {
- uint16_t gs; /* Offset 0 */
- uint16_t fs; /* Offset 2 */
- uint16_t es; /* Offset 4 */
- uint16_t ds; /* Offset 6 */
-
- reg32_t edi; /* Offset 8 */
- reg32_t esi; /* Offset 12 */
- reg32_t ebp; /* Offset 16 */
- reg32_t _unused_esp; /* Offset 20 */
- reg32_t ebx; /* Offset 24 */
- reg32_t edx; /* Offset 28 */
- reg32_t ecx; /* Offset 32 */
- reg32_t eax; /* Offset 36 */
-
- reg32_t eflags; /* Offset 40 */
-} com32sys_t;
-
-The third argument is a pointer to the output register definition, an
-instance of the same structure. The third argument can also be zero
-(NULL).
-
-Since BIOS or Syslinux API calls can generally only manipulate data
-below address 0x100000, a "bounce buffer" in low memory, at least 64K
-in size, is available, to copy data in and out.
-
-The farcall helper function behaves similarly, but takes as its first
-argument the CS:IP (in the form (CS << 16) + IP) of procedure to be
-invoked via a FAR CALL.
-
-The cfarcall helper function takes (CS << 16)+IP, a pointer to a stack
-frame, a size of that stack frame, and returns the return value of EAX
-(which may need to be appropriate truncated by the user.)
-
-Starting in version 4.00, some of these API calls are available as
-protected-mode function calls, using the regparm(3) calling convention
-(the first three argumetns in EAX, EDX, ECX; the rest on the stack.)
-Those functions are defined in struct com32_pmapi, defined in
-<syslinux/pmapi.h>.
-
-
- ++++ SYSLINUX API CALLS +++
-
-Syslinux provides the following API calls. Syslinux 1.xx only
-supported INT 20h - terminate program. [] indicates the first version
-of Syslinux which supported this feature (correctly.)
-
-NOTE: Most of the API functionality is still experimental. Expect to
-find bugs.
-
-
- ++++ DOS-COMPATIBLE API CALLS ++++
-
-INT 20h [1.48] Terminate program
-INT 21h AH=00h [2.00] Terminate program
-INT 21h AH=4Ch [2.00] Terminate program
-
- All of these terminate the program.
-
-
-INT 21h AH=01h [2.01] Get Key with Echo
-
- Reads a key from the console input, with echo to the console
- output. The read character is returned in AL. Extended
- characters received from the keyboard are returned as NUL (00h)
- + the extended character code.
-
-
-INT 21h AH=02h [2.01] Write Character
-
- Writes a character in DL to the console (video and serial)
- output.
-
-
-INT 21h AH=04h [2.01] Write Character to Serial Port
-
- Writes a character in DL to the serial console output
- (if enabled.) If no serial port is configured, this routine
- does nothing.
-
-
-INT 21h AH=08h [2.09] Get Key without Echo
-
- Reads a key fron the console input, without echoing it to the
- console output. The read character is returned in AL.
-
-
-INT 21h AH=09h [2.01] Write DOS String to Console
-
- Writes a DOS $-terminated string in DS:DX to the console.
-
-
-INT 21h AH=0Bh [2.00] Check Keyboard
-
- Returns AL=FFh if there is a keystroke waiting (which can then
- be read with INT 21h, AH=01h or AH=08h), otherwise AL=00h.
-
-
-INT 21h AH=30h [2.00] Check DOS Version
-
- This function returns AX=BX=CX=DX=0, corresponding to a
- hypothetical "DOS 0.0", but the high parts of EAX-EBX-ECX-EDX
- spell "SYSLINUX":
-
- EAX=59530000h EBX=4C530000h ECX=4E490000h EDX=58550000h
-
- This function can thus be used to distinguish running on
- Syslinux from running on DOS.
-
-
- ++++ SYSLINUX-SPECIFIC API CALLS ++++
-
-Syslinux-specific API calls are executed using INT 22h, with a
-function number in AX. INT 22h is used by DOS for internal purposes;
-do not execute INT 22h under DOS.
-
-DOS-compatible function INT 21h, AH=30h can be used to detect if the
-Syslinux API calls are available.
-
-Any register not specifically listed as modified is preserved;
-however, future versions of Syslinux may add additional output
-registers to existing calls.
-
-All calls return CF=0 on success, CF=1 on failure. The noted outputs
-apply if CF=0 only unless otherwise noted. All calls clobber the
-arithmetric flags (CF, PF, AF, ZF, SF and OF) but leave all other
-flags unchanged unless otherwise noted.
-
-
-AX=0001h [2.00] Get Version
-
- Input: AX 0001h
- Output: AX number of INT 22h API functions available
- CH Syslinux major version number
- CL Syslinux minor version number
- DL Syslinux derivative ID (e.g. 32h = PXELINUX)
- ES:SI Syslinux version string
- ES:DI Syslinux copyright string
-
- This API call returns the Syslinux version and API
- information.
-
- Note: before version 3.86, the version string had a leading CR LF
- and the copyright string had a leading space. The strings might
- still contain trailing CR and/or LF.
-
-
-AX=0002h [2.01] Write String
-
- Input: AX 0002h
- ES:BX null-terminated string
- Output: None
-
- Writes a null-terminated string on the console.
-
-
-AX=0003h [2.01] Run command
-
- Input: AX 0003h
- ES:BX null-terminated command string
- Output: Does not return
-
- This API call terminates the program and executes the command
- string as if the user had entered it at the Syslinux command
- line. This API call does not return.
-
-
-AX=0004h [2.01] Run default command
-
- Input: AX 0004h
- Output: Does not return
-
- This API call terminates the program and executes the default
- command string as if the user had pressed Enter alone on the
- Syslinux command line. This API call does not return.
-
-
-AX=0005h [2.00] Force text mode
-
- Input: AX 0005h
- Output: None
-
- If the screen was in graphics mode (due to displaying a splash
- screen using the <Ctrl-X> command in a message file, or
- similar), return to text mode.
-
-
-AX=0006h [2.08] Open file
-
- Input: AX 0006h
- ES:SI null-terminated filename
- Output: SI file handle
- EAX length of file in bytes, or -1
- CX file block size
-
- Open a file for reading. The exact syntax of the filenames
- allowed depends on the particular Syslinux derivative.
-
- The Syslinux file system is block-oriented. The size of a
- block will always be a power of two and no greater than 16K.
-
- Note: Syslinux considers a zero-length file to be nonexistent.
-
- In 3.70 or later, EAX can contain -1 indicating that the file
- length is unknown.
-
- 32-BIT VERSION:
-
- int cs_pm->open_file(const char *filename, struct com32_filedata *data)
-
- filename - null-terminated filename
- data - pointer to a file data buffer
-
- Returns the file handle, or -1 on failure.
- The file data buffer contains block size and file size.
-
-
-AX=0007h [2.08] Read file
-
- Input: AX 0007h
- SI file handle
- ES:BX buffer
- CX number of blocks to read
- Output: SI file handle, or 0 if EOF was reached
- ECX number of bytes read [3.70]
-
- Read blocks from a file. Note that the file handle that is
- returned in SI may not be the same value that was passed in.
-
- If end of file was reached (SI=0), the file was automatically
- closed.
-
- In 3.70 or later, ECX returns the number of bytes read. This
- will always be a multiple of the block size unless EOF is
- reached.
-
- The address of the buffer (ES:BX) should be at least 512-byte
- aligned. Syslinux guarantees at least this alignment for the
- COMBOOT load segment or the COM32 bounce buffer.
-
- Keep in mind that a "file" may be a TFTP connection, and that
- leaving a file open for an extended period of time may result
- in a timeout.
-
- WARNING: Calling this function with an invalid file handle
- will probably crash the system.
-
- 32-BIT VERSION:
-
- size_t cs_pm->read_file(uint16_t *handle, void *buf, size_t blocks)
-
- handle - file handle (input and output, set to zero on end of file)
- buf - buffer to write to
- blocks - number of blocks to read
-
- Returns number of bytes read, or 0 on failure.
-
-
-AX=0008h [2.08] Close file
-
- Input: AX 0008h
- SI file handle
- Output: None
-
- Close a file before reaching the end of file.
-
- WARNING: Calling this function with an invalid file handle
- will probably crash the system.
-
- 32-BIT VERSION:
-
- void cs_pm->close_file(uint16_t handle)
-
- handle - file handle to close
-
-
-AX=0009h [2.00] Call PXE Stack [PXELINUX ONLY]
-
- Input: AX 0009h
- BX PXE function number
- ES:DI PXE parameter structure buffer
- Output: AX PXE return status code
-
- Invoke an arbitrary PXE stack function. On SYSLINUX/ISOLINUX,
- this function returns with an error (CF=1) and no action is
- taken. On PXELINUX, this function always returns with CF=0
- indicating that the PXE stack was successfully invoked; check
- the status code in AX and in the first word of the data buffer
- to determine if the PXE call succeeded or not.
-
- The PXE stack will have the UDP stack OPEN; if you change that
- you cannot call any of the file-related API functions, and
- must restore UDP OPEN before returning to PXELINUX.
-
- PXELINUX reserves UDP port numbers from 49152 to 65535 for its
- own use; port numbers below that range is available.
-
-
-AX=000Ah [2.00] Get Derivative-Specific Information
-
- [SYSLINUX, EXTLINUX]
- Input: AX 000Ah
- CL 9 (to get a valid return in CL for all versions)
- Output: AL 31h (SYSLINUX), 34h (EXTLINUX)
- DL drive number
- CL sector size as a power of 2 (9 = 512 bytes) [3.35]
- CH mode [3.73]
- 1 = CBIOS mode
- 2 = EBIOS mode
- ES:BX pointer to partition table entry (if DL >= 80h)
- FS:SI pointer to initial ES:DI value [3.53]
- GS:DI pointer to partition offset (QWORD) [4.00]
-
- Note: This function was broken in EXTLINUX 3.00-3.02.
-
- On boot, ES:DI is supposed to point to the BIOS $PnP
- structure, although in practice most operating systems
- will search for it in memory. However, preserving
- this while chainloading is probably a good idea.
-
- Note that FS:SI is a pointer to a memory location
- containing the original ES:DI value, not the value
- itself.
-
-
- [PXELINUX]
- Input: AX 000Ah
- Output: AL 32h (PXELINUX)
- DX PXE API version detected (DH=major, DL=minor)
- ECX Local IP number (network byte order) [3.85]
- ES:BX pointer to PXENV+ or !PXE structure
- FS:SI pointer to original stack with invocation record
- GS:DI pointer to network information [4.00]
-
- Note: DX notes the API version detected by PXELINUX,
- which may be more conservative than the actual version
- available. For exact information examine the API
- version entry in the PXENV+ structure, or the API
- version entries in the ROMID structures pointed from
- the !PXE structure.
-
- PXELINUX will use, and provide, the !PXE structure
- over the PXENV+ structure. Examine the structure
- signature to determine which particular structure was
- provided.
-
- The FS:SI pointer points to the top of the original stack
- provided by the PXE stack, with the following values
- pushed at the time PXELINUX is started:
-
- [fs:si+0] GS <- top of stack
- [fs:si+2] FS
- [fs:si+4] ES
- [fs:si+6] DS
- [fs:si+8] EDI
- [fs:si+12] ESI
- [fs:si+16] EBP
- [fs:si+20] -
- [fs:si+24] EBX
- [fs:si+28] EDX
- [fs:si+32] ECX
- [fs:si+36] EAX
- [fs:si+40] EFLAGS
- [fs:si+44] PXE return IP <- t.o.s. when PXELINUX invoked
- [fs:si+46] PXE return CS
-
- GS:DI points to a structure of the following form:
-
- [gs:di+0] 4 - IPv4
- [gs:di+4] My IP
- [gs:di+8] Boot server IP
- [gs:di+12] Gateway IP
- [gs:di+16] Netmask
-
- [ISOLINUX]
- Input: AX 000Ah
- Output: AL 33h (ISOLINUX)
- DL drive number
- CL 11 (sector size as a power of 2) [3.35]
- CH mode [3.73]
- 0 = El Torito
- 1 = Hybrid (hard disk), CBIOS mode
- 2 = Hybrid (hard disk), EBIOS mode
- ES:BX pointer to El Torito spec packet
- FS:SI pointer to initial ES:DI value [3.53]
- GS:DI pointer to partition offset (QWORD) [4.00]
-
- Note: Some very broken El Torito implementations do
- not provide the spec packet information. If so, ES:BX
- may point to all zeroes or to garbage. Call INT 13h,
- AX=4B01h to obtain the spec packet directly from the
- BIOS if necessary.
-
-
-AX=000Bh [2.00] Get Serial Console Configuration
-
- Input: AX 000Bh
- Output: DX serial port I/O base (e.g. 3F8h = COM1...)
- CX baud rate divisor (1 = 115200 bps, 2 = 57600 bps...)
- BX flow control configuration bits (see syslinux.txt)
- -> bit 15 is set if the video console is disabled
-
- If no serial port is configured, DX will be set to 0 and the
- other registers are undefined.
-
-
-AX=000Ch [2.00] Perform final cleanup
- Input: AX 000Ch
- DX derivative-specific flags (0000h = clean up all)
- Output: None
-
- This routine performs any "final cleanup" the boot loader
- would normally perform before loading a kernel, such as
- unloading the PXE stack in the case of PXELINUX. AFTER
- INVOKING THIS CALL, NO OTHER API CALLS MAY BE INVOKED, NOR MAY
- THE PROGRAM TERMINATE AND RETURN TO THE BOOT LOADER. This
- call basically tells the boot loader "get out of the way, I'll
- handle it from here."
-
- For COM32 images, the boot loader will continue to provide
- interrupt and BIOS call thunking services as long its memory
- areas (0x0800-0xffff, 0x100000-0x100fff) are not overwritten.
- MAKE SURE TO DISABLE INTERRUPTS, AND INSTALL NEW GDT AND IDTS
- BEFORE OVERWRITING THESE MEMORY AREAS.
-
- The permissible values for DX is an OR of these values:
-
- SYSLINUX: 0000h Normal cleanup
-
- PXELINUX: 0000h Normal cleanup
- 0003h Keep UNDI and PXE stacks loaded
-
- ISOLINUX: 0000h Normal cleanup
-
- EXTLINUX: 0000h Normal cleanup
-
- All other values are undefined, and may have different
- meanings in future versions of Syslinux.
-
-
-AX=000Dh [2.08] Obsoleted in 3.80
-
-
-AX=000Eh [2.11] Get configuration file name
- Input: AX 0000Eh
- Output: ES:BX null-terminated file name string
-
- Returns the name of the configuration file. Note that it is
- possible that the configuration file doesn't actually exist.
-
-
-AX=000Fh [3.00] Get IPAPPEND strings [PXELINUX]
- Input: AX 000Fh
- Output: CX number of strings (currently 2)
- ES:BX pointer to an array of NEAR pointers in
- the same segment, one for each of the above
- strings
-
- Returns the same strings that the "ipappend" option would have
- added to the command line, one for each bit of the "ipappend"
- flag value, so entry 0 is the "ip=" string and entry 1 is the
- "BOOTIF=" string.
-
-
-AX=0010h [3.00] Resolve hostname [PXELINUX]
- Input: AX 0010h
- ES:BX pointer to null-terminated hostname
- Output: EAX IP address of hostname (zero if not found)
-
- Queries the DNS server(s) for a specific hostname. If the
- hostname does not contain a dot (.), the local domain name
- is automatically appended.
-
- This function only return CF=1 if the function is not
- supported. If the function is supported, but the hostname did
- not resolve, it returns with CF=0, EAX=0.
-
- The IP address is returned in network byte order, i.e. if the
- IP address is 1.2.3.4, EAX will contain 0x04030201. Note that
- all uses of IP addresses in PXE are also in network byte order.
-
-
-AX=0011h [3.05] Obsoleted in 3.80
-
-
-AX=0012h [3.50] Obsoleted in 3.80
-
-
-AX=0013h [3.08] Idle loop call
- Input: AX 0013h
- Output: None
-
- Call this routine while sitting in an idle loop. It performs
- any periodic activities required by the filesystem code. At
- the moment, this is a no-op on all derivatives except
- PXELINUX, where it executes PXE calls to answer ARP queries.
-
- Starting with version 3.10, this API call harmlessly returns
- failure (CF=1) if invoked on a platform which does not need
- idle calls. Additionally, it's safe to call this API call on
- previous Syslinux versions (2.00 or later); it will just
- harmlessly fail. Thus, if this call returns failure (CF=1),
- it means that there is no technical reason to call this
- function again, although doing so is of course safe.
-
-
-AX=0014h [3.10] Local boot [PXELINUX, ISOLINUX]
- Input: AX 0014h
- DX Local boot parameter
- Output: Does not return
-
- This function invokes the equivalent of the "localboot"
- configuration file option. The parameter in DX is the same
- parameter as would be entered after "localboot" in the
- configuration file; this parameter is derivative-specific --
- see syslinux.txt for the definition.
-
-
-AX=0015h [3.10] Get feature flags
- Input: AX 0015h
- Output: ES:BX pointer to flags in memory
- CX number of flag bytes
-
- This function reports whether or not this Syslinux version and
- derivative supports specific features. Keep in mind that
- future versions might have more bits; remember to treat any
- bits beyond the end of the array (as defined by the value in
- CX) as zero.
-
- Currently the following feature flag is defined:
-
- Byte Bit Definition
- ----------------------------------------------------
- 0 0 Local boot (AX=0014h) supported
- 1 Idle loop call (AX=0013h) is a no-op
-
- All other flags are reserved.
-
-
-AX=0016h [3.10] Run kernel image
- Input: AX 0016h
- DS:SI Filename of kernel image (zero-terminated string)
- ES:BX Command line (zero-terminated string)
- ECX IPAPPEND flags [PXELINUX]
- EDX Type of file (since 3.50)
- Output: Does not return if successful; returns with CF=1 if
- the kernel image is not found.
-
- This function is similiar to AX=0003h Run command, except that
- the filename and command line are treated as if specified in a
- KERNEL and APPEND statement of a LABEL statement, which means:
-
- - The filename has to be exact; no variants are tried;
- - No global APPEND statement is applied;
- - ALLOWOPTIONS and IMPLICIT statements in the configuration
- file do not apply. It is therefore important that the
- COMBOOT module doesn't allow the end user to violate the
- intent of the administrator.
-
- Additionally, this function returns with a failure if the file
- doesn't exist, instead of returning to the command line. (It
- may still return to the command line if the image is somehow
- corrupt, however.)
-
- The file types are defined as follows:
-
- Equivalent
- EDX Config Extensions Type of file
- 0 KERNEL Determined by filename extension
- 1 LINUX none Linux kernel image
- 2 BOOT .bs .bin Bootstrap program
- 3 BSS .bss Boot sector with patch [SYSLINUX]
- 4 PXE .0 PXE Network Bootstrap Prog [PXELINUX]
- 5 FDIMAGE .img Floppy disk image [ISOLINUX]
- 6 COMBOOT .com .cbt 16-bit COMBOOT program
- 7 COM32 .c32 COM32 program
- 8 CONFIG Configuration file
-
-
-AX=0017h [3.30] Report video mode change
- Input: AX 0017h
- BX Video mode flags
- Bit 0: graphics mode
- Bit 1: non-default mode
- Bit 2: VESA mode
- Bit 3: text functions not supported
- CX For graphics modes, pixel columns
- DX For graphics modes, pixel rows
- Output: None
-
- This function is used to report video mode changes to
- Syslinux. It does NOT actually change the video mode, but
- rather, allows Syslinux to take appropriate action in response
- to a video mode change. Modes that cannot be exited either
- with the conventional BIOS mode set command (INT 10h, AH=00h)
- or the VESA VBE mode set command (INT 10h, AX=4F02h) should
- not be used.
-
- This function returns with a failure if BX contains any bits
- which are undefined in the current version of Syslinux.
-
- The following bits in BX are currently defined:
-
- Bit 0: graphics mode
-
- Indicates that the mode is a graphics mode, as opposed
- to a text mode.
-
- Bit 1: non-standard mode
-
- A non-standard mode is any mode except text mode and
- graphics mode 0012h (VGA 640x480, 16 color.)
-
- Bit 2: VESA mode
-
- This mode is a VESA mode, and has to be exited with
- the VESA VBE API (INT 10h, AX=4F02h) as opposed to the
- conventional BIOS API (INT 10h, AH=00h).
-
- Bit 3: Text functions not supported
-
- This indicates that the BIOS text output functions
- (INT 10h, AH=02h, 03h, 06h, 09h, 0Eh, 11h) don't work.
- If this bit is set, Syslinux will reset the mode
- before printing any characters on the screen.
-
- This is common for VESA modes.
-
-
-AX=0018h [3.30] Query custom font
- Input: AX 0018h
- Output: AL Height of custom font in scan lines, or zero
- ES:BX Pointer to custom font in memory
-
- This call queries if a custom display font has been loaded via
- the "font" configuration file command. If no custom font has
- been loaded, AL contains zero.
-
-
-AX=0019h [3.50] Read disk [SYSLINUX, ISOLINUX, EXTLINUX]
- Input: AX 0019h
- EDX Sector number (LSW)
- ESI Sector number (MSW) [4.00]
- EDI Reserved - MUST BE ZERO
- CX Sector count
- ES:BX Buffer address
- Output: None
-
- Read disk blocks from the active filesystem (partition); for
- disks, sector number zero is the boot sector. For ISOLINUX,
- this call reads the CD-ROM.
-
- For compatiblity with all systems, the buffer should
- *neither* cross 64K boundaries, *nor* wrap around the segment.
-
- This routine reports "boot failed" (and does not return) on
- disk error.
-
- Note: for ISOLINUX in hybrid mode, this call uses simulated
- 2048-byte CD-ROM sector numbers.
-
-
-AX=001Ah [3.50] Obsoleted in 3.80
-
-
-AX=001Bh [3.50] Obsoleted in 3.80
-
-
-AX=001Ch [3.60] Get pointer to auxilliary data vector
- Input: AX 001Ch
- Output: ES:BX Auxilliary data vector
- CX Size of the ADV (currently 500 bytes)
-
- The auxillary data vector is a tagged data structure used
- to carry a small amount of information (up to 500 bytes) from
- one boot to another.
-
-
-AX=001Dh [3.60] Write auxilliary data vector
- Input: AX 001Dh
- Output: None
-
- Write the auxilliary data vector back to disk. Returns
- failure for non-disk-based derivatives unless the "auxdata"
- configuration command is used to specify a disk location
- (not yet implemented.)
-
- In a future version, PXELINUX may end up attempting to save
- the ADV on the server via TFTP write.
-
-
-AX=001Eh [3.74] Keyboard remapping table
- Input: AX 001Eh
- DX 0000h - all other values reserved
- Output: AX format version (1)
- CX length in bytes (256)
- ES:BX pointer to keyboard table
-
- This call queries the keyboard remapping table. For the current
- version, the format code is always 1 and the length is always
- 256. This version can be updated simply by overwriting the version
- in memory; this may not be true in the future.
-
-
-AX=001Fh [3.74] Get current working directory
- Input: AX 0001Fh
- Output: ES:BX null-terminated directory name string
-
- Returns the current working directory.
-
-
-AX=0020h [3.74] Obsoleted in 4.00
-AX=0021h [3.74] Obsoleted in 4.00
-AX=0022h [3.74] Obsoleted in 4.00
-
- These three functions provided opendir/readdir/closedir
- functionality in the late 3.xx series. They have been
- replaced by the protected-mode interface.
-
-
-AX=0023h [3.80] Get shuffler parameters
- Input: AX 0023h
- Output: CX size of shuffler "safe area" in bytes
- Other registers reserved for future use
-
- This call gives the size of the required shuffler "safe area",
- in bytes; for call 0024h. In the future, it may provide
- additional parameters.
-
-
-AX=0024h [3.80] Cleanup, shuffle and boot, raw version
- Input: AX 0024h
- DX derivative-specific flags (see function 000Ch)
- EDI shuffle descriptor list safe area
- ESI shuffle descriptor list source
- ECX byte count of shuffle descriptor list
- Output: Does not return
-
- This routine performs final cleanup, then performs a sequence
- of copies, and jumps to a specified real mode entry point.
- This is a more general version of function 000Dh, which can
- also be used to load other types of programs.
-
- Unlike previous obsolete versions of this function, there are
- no restrictions that copies must not touch memory below
- address 7C00h. Either the shuffle descriptor list or the safe
- area (or both) may be located in high memory.
-
- ESI points to a list of descriptors each of the form:
-
- Offset Size Meaning
- 0 dword destination address
- 4 dword source address (-1 = zero)
- 8 dword length in bytes (0 = end of list)
-
- The copies are overlap-safe, like memmove().
-
- Before actually executing the move list, the list is moved to
- the address specified in EDI. The caller is responsibe to
- ensure that the moved descriptor list plus a "safe area"
- immediately afterwards (the size of which is specified by
- function 0023h) is not disturbed by the copy sequence. It is,
- however, safe to overwrite descriptors already consumed.
-
- If the source address is -1 (FFFFFFFFh) then the block
- specified by the destination address and the length is set to
- all zero.
-
- The list is terminated by an entry with length 0. For that
- entry, the destination is used as an entry point, and the
- source represents the type of entry point:
-
- 0 16-bit protected mode (dst is CS.base)
- 1 Flat 32-bit protected mode (dst is EIP)
-
- This routine does not set up any GPR register state
- whatsoever, including stack. It is the responsibility of the
- caller to make sure the entry point provided sets up any
- registers needed.
-
- For mode 0 (16-bit real mode), EAX will contain CR0 with bit 0
- masked out, suitable for loading into CR0 to immediately enter
- real mode. Note: if real-mode entry is planned,
- (CS.base & 0xfff0000f) should == 0 for compatibility with KVM,
- and possibly other virtualization solutions.
-
- In both mode 0 and mode 1, the data segments will be loaded
- with read/write data segments, matching the respective code
- segment. For mode 0, B=0 and the limits will be 64K, for mode
- 1, B=1 and the limits will be 4 GB.
-
-
- ++++ 32-BIT ONLY API CALLS ++++
-
-void *cs_pm->lmalloc(size_t bytes)
-
- Allocate a buffer in low memory (below 1 MB).
-
-
-void cs_pm->lfree(void *ptr)
-
- Free a buffer allocated with cs_pm->lmalloc().
-
-
-DIR *cs_pm->opendir(const char *pathname)
-
- Open a directory.
-
-
-struct dirent *cs_pm->readdir(DIR *dir)
-
- Read an entry from a directory. The entry is returned in a
- static buffer.
-
-
-int cs_pm->closedir(DIR *dir)
-
- Close a directory.
diff --git a/doc/cptime.txt b/doc/cptime.txt
new file mode 100644
index 00000000..982dbf7c
--- /dev/null
+++ b/doc/cptime.txt
@@ -0,0 +1,50 @@
+= cptime.c32(1) =
+:doctype: manpage
+:author: Gene Cumm
+:email: gene.cumm@gmail.com
+:revdate: 2011-12-17
+
+
+== NAME ==
+cptime.c32 - times the copy off (read) of a file
+
+
+== SYNOPSIS ==
+*cptime.c32* ['OPTIONS'] 'FILE'...
+
+
+== DESCRIPTION ==
+Times the copy off (read) of a file, optionally computes/displays
+transfer rates(on by default) with an adjustable transfer size and
+maximum transfer length.
+
+
+== OPTIONS ==
+*-b* 'SIZE'::
+ use 'SIZE' for transfer size; defaults to 2048 for COM32
+
+*-l*::
+ long output mode; default; reverses *-s*
+
+*-n* 'LEN'::
+ maximum length to fetch; defaults to whole file
+
+*-q*::
+ quiet (normal/non-verbose) mode; default; reverses *-v*
+
+*-s*::
+ simple output mode
+
+*-v*::
+ verbose mode
+
+The same mode is used for all files.
+
+
+== AUTHOR ==
+{author} <{email}>
+
+== COPYRIGHT ==
+Copyright \(C) 2011 {author}. Free use of this software is granted under
+the terms of the GNU General Public License (GPL).
+
diff --git a/doc/extlinux.txt b/doc/extlinux.txt
index 9b267018..d8cbb5df 100644
--- a/doc/extlinux.txt
+++ b/doc/extlinux.txt
@@ -49,8 +49,8 @@ slight modifications.
5. EXTLINUX now has "boot-once" support. The boot-once information is
stored in an on-disk datastructure, part of extlinux.sys, called
the "Auxillary Data Vector". The Auxilliary Data Vector is also
- available to COMBOOT/COM32 modules that want to store small amounts
- of information.
+ available to COM32 modules that want to store small amounts of
+ information.
To set the boot-once information, do:
diff --git a/doc/mboot.txt b/doc/mboot.txt
index ef00ca5c..4f8c7b10 100644
--- a/doc/mboot.txt
+++ b/doc/mboot.txt
@@ -2,7 +2,7 @@
mboot.c32
---------
-mboot.c32 is a 32-bit comboot module that allows Syslinux and its
+mboot.c32 is a 32-bit ELF module that allows Syslinux and its
variants to load and boot kernels that use the Multiboot standard
(e.g. the Xen virtual machine monitor, and the Fiasco and GNU Mach
microkernels).
diff --git a/doc/pxelinux.txt b/doc/pxelinux.txt
index 69c1a645..4dbb152e 100644
--- a/doc/pxelinux.txt
+++ b/doc/pxelinux.txt
@@ -3,12 +3,16 @@
A bootloader for Linux using the PXE network booting protocol
Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+ Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
This program is provided under the terms of the GNU General Public
License, version 2 or, at your option, any later version. There is no
warranty, neither expressed nor implied, to the function of this
program. Please see the included file COPYING for details.
+This documentation file is slightly out of date; please check the NEWS
+file for changes.
+
----------------------------------------------------------------------
PXELINUX is a Syslinux derivative, for booting Linux off a network
@@ -99,25 +103,22 @@ boot, if you have such a setup. MTFTP server setup is beyond the
scope of this document.
- ++++ gPXE-ENHANCED VARIANTS ++++
+ ++++ HTTP AND FTP DOWNLOADS ++++
-gPXE can be used to enhance PXELINUX's functionality to also include
-HTTP transfers, greatly increasing load speed and allowing for standard
-HTTP scripts to present PXELINUX's configuration file. pxelinux.0 is
-the plain variant. gpxelinux.0 (included as of 3.70) is gPXE's
-undionly.kkpxe, pxelinux.0 and a script to run pxelinux.0. gpxelinuxk.0
-(included as of 4.04) is gPXE's undionly.kpxe, pxelinux.0 and a script
-to run pxelinux.0. gpxelinuxk.0 should only be used with systems that
-are incompatible with gpxelinux.0 as it prevents certain functionality
-from working (LOCALBOOT with a type not equal to -1) and is incompatible
-with certain hardware, PXE stacks and network setups.
+Since version 5.10, native pxelinux.0 can support HTTP and FTP
+transfers, greatly increasing load speed and allowing for standard
+HTTP scripts to present PXELINUX's configuration file. To use http or
+ftp, use standard URL syntax as filename; use the DHCP options below
+to transmit a suitable URL prefix to the client, or use the
+"pxelinux-options" tool provided in the utils directory to program it
+directly into the pxelinux.0 file.
++++ SETTING UP THE TFTP SERVER ++++
-PXELINUX currently requires that the boot server has a TFTP server
-which supports the "tsize" TFTP option (RFC 1784/RFC 2349). The
-"tftp-hpa" TFTP server, which support options, is available at:
+For best results, use a TFTP server which supports the "tsize" TFTP
+option (RFC 1784/RFC 2349). The "tftp-hpa" TFTP server, which support
+options, is available at:
http://www.kernel.org/pub/software/network/tftp/
ftp://www.kernel.org/pub/software/network/tftp/
@@ -343,7 +344,15 @@ If you used this from a client whose Ethernet address was
"/tftpboot/pxelinux.cfg/1:58:fa:84:cf:55:e".
- ++++ ALTERNATE TFTP SERVERS ++++
+ ++++ HARDCODED OPTIONS ++++
+
+Since version 3.83, the program "pxelinux-options" can be used to
+hard-code DHCP options into the pxelinux.0 image file; this is
+sometimes useful when the DHCP server is under different
+administrative control.
+
+
+ ++++ ALTERNATE TFTP SERVERS AND URL SYNTAX ++++
PXELINUX supports the following special pathname conventions:
@@ -365,6 +374,8 @@ usage. However, if you happen to have an environment for which the
special treatment of :: is a problem, please contact the Syslinux
mailing list.
+Since version 4.00, PXELINUX also supports standard URL syntax.
+
++++ SOME NOTES ++++
diff --git a/doc/syslinux.txt b/doc/syslinux.txt
index 033a1ec0..fd7396e3 100644
--- a/doc/syslinux.txt
+++ b/doc/syslinux.txt
@@ -153,9 +153,14 @@ APPEND options...
usually permitting explicitly entered kernel options to override
them. This is the equivalent of the LILO "append" option.
-IPAPPEND flag_val [PXELINUX only]
- The IPAPPEND option is available only on PXELINUX. The
- flag_val is an OR of the following options:
+SYSAPPEND bitmask
+IPAPPEND bitmask
+
+ The SYSAPPEND option was introduced in Syslinux 5.10; it is an
+ enhancement of a previous option IPAPPEND which was only
+ available on PXELINUX. bitmask is interpreted as decimal format
+ unless prefixed with "0x" for hexadecimal or "0" (zero) for
+ octal.
1: indicates that an option of the following format
should be generated and added to the kernel command line:
@@ -169,6 +174,8 @@ IPAPPEND flag_val [PXELINUX only]
the lease acquired by the PXE BIOS will expire, making the
IP address available for reuse by the DHCP server.
+ This option is empty for non-PXELINUX.
+
2: indicates that an option of the following format
should be generated and added to the kernel command line:
@@ -180,6 +187,8 @@ IPAPPEND flag_val [PXELINUX only]
This allows an initrd program to determine from which
interface the system booted.
+ This option is empty for non-PXELINUX.
+
4: indicates that an option of the following format
should be generated and added to the kernel command line:
@@ -187,14 +196,105 @@ IPAPPEND flag_val [PXELINUX only]
... in lower case hexadecimal in the format normally used for
UUIDs (same as for the configuration file; see pxelinux.txt.)
+ This may not be available if no valid UUID is found on the
+ system.
+
+ 8: indicate the CPU family and certain particularly
+ significant CPU feature bits:
+
+ CPU=<family><features>
+
+ The <family> is a single digit from 3 (i386) to 6 (i686 or
+ higher.) The following CPU feature are currently reported;
+ additional flags may be added in the future:
+
+ P Physical Address Extension (PAE)
+ V Intel Virtualization Technology (VT/VMX)
+ T Intel Trusted Exection Technology (TXT/SMX)
+ X Execution Disable (XD/NX)
+ L Long Mode (x86-64)
+ S AMD SMX virtualization
+
+ This was added in 5.10.
+
+ The following strings are derived from DMI/SMBIOS information
+ if available; these are all new in version 5.10:
+
+ Bit String Significance
+ -------------------------------------------------------------
+ 0x00010 SYSVENDOR= System vendor name
+ 0x00020 SYSPRODUCT= System product name
+ 0x00040 SYSVERSION= System version
+ 0x00080 SYSSERIAL= System serial number
+ 0x00100 SYSSKU= System SKU
+ 0x00200 SYSFAMILY= System family
+ 0x00400 MBVENDOR= Motherboard vendor name
+ 0x00800 MBVERSION= Motherboard version
+ 0x01000 MBSERIAL= Motherboard serial number
+ 0x02000 MBASSET= Motherboard asset tag
+ 0x04000 BIOSVENDOR= BIOS vendor name
+ 0x08000 BIOSVERSION= BIOS version
+ 0x10000 SYSFF= System form factor
+
+ If these strings contain whitespace they are replaced with
+ underscores (_).
+
+ The system form factor value is a number defined in the SMBIOS
+ specification, available at http://www.dmtf.org/. As of
+ version 2.7.1 of the specification, the following values are
+ defined:
+
+ 1 Other
+ 2 Unknown
+ 3 Desktop
+ 4 Low profile desktop
+ 5 Pizza box
+ 6 Mini tower
+ 7 Tower
+ 8 Portble
+ 9 Laptop
+ 10 Notebook
+ 11 Handheld
+ 12 Docking station
+ 13 All-in-one
+ 14 Subnotebook
+ 15 Space-saving
+ 16 Lunch box
+ 17 Main server chassis
+ 18 Expansion chassis
+ 19 Subchassis
+ 20 Bus expansion chassis
+ 21 Peripheral chassis
+ 22 RAID chassis
+ 23 Rack mount chasss
+ 24 Sealed-case PC
+ 25 Multi-system chassis
+ 26 Compact PCI
+ 27 Advanced TCI
+ 28 Blade
+ 29 Blade enclosure
+
+SENDCOOKIES bitmask [PXELINUX only]
+
+ When downloading files over http, the SYSAPPEND strings are
+ prepended with _Syslinux_ and sent to the server as cookies.
+ The cookies are URL-encoded; whitespace is *not* replaced with
+ underscores.
+
+ This command limits the cookies send; 0 means no cookies. The
+ default is -1, meaning send all cookies.
+
+ This option is "sticky" and is not automatically reset when
+ loading a new configuration file with the CONFIG command.
LABEL label
KERNEL image
APPEND options...
- IPAPPEND flag_val [PXELINUX only]
+ SYSAPPEND flag_val [5.10+]
+ IPAPPEND flag_val [5.10+ or PXELINUX only]
Indicates that if "label" is entered as the kernel to boot,
Syslinux should instead boot "image", and the specified APPEND
- and IPAPPEND options should be used instead of the ones
+ and SYSAPPEND options should be used instead of the ones
specified in the global section of the file (before the first
LABEL command.) The default for "image" is the same as
"label", and if no APPEND is given the default is to use the
@@ -214,7 +314,7 @@ LABEL label
append myoptions
Note: The "kernel" doesn't have to be a Linux kernel; it can
- be a boot sector or a COMBOOT file (see below.)
+ be a boot sector (see below.)
Since version 3.32 label names are no longer mangled into DOS
format (for SYSLINUX.)
@@ -226,7 +326,6 @@ LABEL label
BSS image - BSS image (.bss)
PXE image - PXE Network Bootstrap Program (.0)
FDIMAGE image - Floppy disk image (.img)
- COMBOOT image - COMBOOT program (.com, .cbt)
COM32 image - COM32 program (.c32)
CONFIG image - New configuration file
Using one of these keywords instead of KERNEL forces the
@@ -329,7 +428,7 @@ ONERROR kernel options...
xyzzy plugh foo bar baz
-SERIAL port [[baudrate] flowcontrol]
+SERIAL port [baudrate [flowcontrol]]
Enables a serial port to act as the console. "port" is a
number (0 = /dev/ttyS0 = COM1, etc.) or an I/O port address
(e.g. 0x3F8); if "baudrate" is omitted, the baud rate defaults
@@ -371,6 +470,9 @@ SERIAL port [[baudrate] flowcontrol]
responsiveness without setting the NOHALT option, but could
potentially cause problems with buggy BIOSes.
+ This option is "sticky" and is not automatically reset when
+ loading a new configuration file with the CONFIG command.
+
NOHALT flag_val
If flag_val is 1, don't halt the processor while idle.
Halting the processor while idle significantly reduces the
@@ -450,6 +552,15 @@ F12 filename
compatibility with earlier versions, F10 can also be entered as
<Ctrl-F>0.
+PATH path
+ Specify a colon-separated (':') list of directories to search
+ when attempting to load modules. This directive is useful for
+ specifying the directories containing the lib*.c32 library
+ files as other modules may be dependent on these files, but
+ may not reside in the same directory. The list of directories
+ is searched in order. Please see the section below on PATH
+ RULES.
+
Blank lines are ignored.
Note that the configuration file is not completely decoded. Syntax
@@ -557,17 +668,15 @@ The command line prompt supports the following keystrokes:
<Ctrl-N> display network information (PXELINUX only)
- ++++ COMBOOT IMAGES AND OTHER OPERATING SYSTEMS ++++
+ ++++ OTHER OPERATING SYSTEMS ++++
This version of Syslinux supports chain loading of other operating
-systems (such as MS-DOS and its derivatives, including Windows 95/98),
-as well as COMBOOT-style standalone executables (a subset of DOS .COM
-files; see separate section below.)
+systems (such as MS-DOS and its derivatives, including Windows 95/98).
Chain loading requires the boot sector of the foreign operating system
to be stored in a file in the root directory of the filesystem.
-Because neither Linux kernels, boot sector images, nor COMBOOT files
-have reliable magic numbers, Syslinux will look at the file extension.
+Because neither Linux kernels, nor boot sector images have reliable
+magic numbers, Syslinux will look at the file extension.
The following extensions are recognized (case insensitive):
none or other Linux kernel image
@@ -575,9 +684,7 @@ The following extensions are recognized (case insensitive):
.bin "CD boot sector" [ISOLINUX only]
.bs Boot sector [SYSLINUX only]
.bss Boot sector, DOS superblock will be patched in [SYSLINUX only]
- .c32 COM32 image (32-bit COMBOOT)
- .cbt COMBOOT image (not runnable from DOS)
- .com COMBOOT image (runnable from DOS)
+ .c32 COM32 image (32-bit ELF)
.img Disk image [ISOLINUX only]
For filenames given on the command line, Syslinux will search for the
@@ -586,7 +693,7 @@ filename is not found. Filenames in KERNEL statements must be fully
qualified.
If this is specified with one of the keywords LINUX, BOOT, BSS,
-FDIMAGE, COMBOOT, COM32, or CONFIG instead of KERNEL, the filetype is
+FDIMAGE, COM32, or CONFIG instead of KERNEL, the filetype is
considered to be the one specified regardless of the filename.
@@ -666,17 +773,6 @@ syslinux.exe instead.
copy initrd.gz a:
- ++++ COMBOOT EXECUTABLES ++++
-
-Syslinux supports simple standalone programs, using a file format
-similar to DOS ".com" files. A 32-bit version, called COM32, is also
-provided. A simple API provides access to a limited set of filesystem
-and console functions.
-
-See the file comboot.txt for more information on COMBOOT and COM32
-programs.
-
-
++++ NOVICE PROTECTION ++++
Syslinux will attempt to detect booting on a machine with too little
@@ -768,6 +864,32 @@ In recent versions of Linux, this ID is available as
/proc/sys/kernel/bootloader_type.
+ ++++ PATH RULES ++++
+
+The current working directory is *always* searched first, before PATH,
+when attempting to open a filename. The current working directory is
+not affected when specifying a file with an absolute path. For
+example, given the following file system layout,
+
+ /boot/
+ /bin/
+ ls.c32
+ libls.c32
+ /foo/
+ libls.c32
+
+assuming that the current working directory is /boot/foo, and assuming
+that libls.c32 is a dependency of ls.c32, executing /boot/bin/ls.c32
+will cause /boot/foo/libls.c32 to be loaded, not /boot/bin/libls.c32,
+even if /boot/bin is specified in the PATH directive of a config file.
+
+The reason that things work this way is that typically a user will
+install all library files in the Syslinux installation directory, as
+specified with the --directory installer option. This method allows
+the user to omit the PATH directive from their config file and still
+have things work correctly.
+
+
++++ BUG REPORTS ++++
I would appreciate hearing of any problems you have with Syslinux. I
diff --git a/dos/argv.c b/dos/argv.c
index 056aae59..da283666 100644
--- a/dos/argv.c
+++ b/dos/argv.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2013 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -28,48 +29,81 @@
/*
* argv.c
*
- * Parse a single C string into argc and argv (argc is return value.)
+ * Parse the MS-DOS command line into argc and argv (argc is return value.)
* memptr points to available memory.
*/
#include <inttypes.h>
#include <stddef.h>
-#include <stdio.h>
+#include <stdbool.h>
+#include "mystuff.h"
#define ALIGN_UP(p,t) ((t *)(((uintptr_t)(p) + (sizeof(t)-1)) & ~(sizeof(t)-1)))
extern char __heap_start[];
void *__mem_end = &__heap_start; /* Global variable for use by malloc() */
-int __parse_argv(char ***argv, const char *str)
+int __parse_argv(char ***argv)
{
char *mem = __mem_end;
- const char *p = str;
+ const char *str, *p;
char *q = mem;
- char *r;
+ char c, *r;
char **arg;
- int wasspace = 0;
- int argc = 1;
+ bool wasspace;
+ int argc;
+ int len;
+ size_t offs;
+ int nulls;
+ uint16_t nstr;
- /* First copy the string, turning whitespace runs into nulls */
+ /* Find and copy argv[0] after the environment block */
+ set_fs(_PSP.environment);
+ offs = 0;
+ nulls = 0;
+ do {
+ if (get_8_fs(offs++) == '\0')
+ nulls++;
+ else
+ nulls = 0;
+ } while (nulls < 2);
+
+ nstr = get_16_fs(offs);
+ offs += 2;
+
+ /* Copy the null-terminated filename string */
+ if (nstr >= 1) {
+ while ((c = get_8_fs(offs++)))
+ *q++ = c;
+ }
+ *q++ = '\0';
+
+ /* Now for the command line tail... */
+
+ len = _PSP.cmdlen;
+ str = _PSP.cmdtail;
+ argc = 1;
+ wasspace = true;
+
+ /* Copy the command tail, turning whitespace runs into nulls */
for (p = str;; p++) {
- if (*p <= ' ') {
+ if (!len || *p <= ' ') {
if (!wasspace) {
- wasspace = 1;
+ wasspace = true;
*q++ = '\0';
}
} else {
if (wasspace) {
argc++;
- wasspace = 0;
+ wasspace = false;
}
*q++ = *p;
}
- /* This test is AFTER we have processed the null byte;
+ /* This test is AFTER we have processed the end byte;
we treat it as a whitespace character so it terminates
the last argument */
- if (!*p)
+ if (!len--)
break;
}
@@ -78,7 +112,7 @@ int __parse_argv(char ***argv, const char *str)
*argv = arg;
*arg++ = mem; /* argv[0] */
- q--; /* Point q to final null */
+ q--; /* Point q to terminal character */
for (r = mem; r < q; r++) {
if (*r == '\0') {
*arg++ = r + 1;
diff --git a/dos/crt0.S b/dos/crt0.S
index 3be57125..66b52c06 100644
--- a/dos/crt0.S
+++ b/dos/crt0.S
@@ -9,7 +9,7 @@
.type _start,@function
_start:
# Align the stack and make sure the high half is zero
- andl $0xfff8,%esp
+ andl $0xfffc,%esp
# DS, ES points to the PSP at this point
pushw %es # Save PSP pointer
@@ -26,16 +26,27 @@ _start:
shrw $2,%cx
rep ; stosl
- # Copy the command line into our own segment
+ # Copy the PSP into our own segment
popw %fs # FS -> PSP
- movw $_cmdline,%di
- movzbw %fs:0x80,%cx
- movw $0x81,%si
- fs ; rep ; movsb
- # Already zero-terminated since we're writing into clean bss
+ movw $_PSP,%di
+ xorw %si,%si
+ movw $0x40,%cx
+ fs ; rep ; movsl
+ # Verify that this is a supportable DOS version
+ movw $0x3001,%ax
+ int $0x21
+ xchgb %ah,%al
+ movw %ax,dos_version
+ cmpw $0x0314,%ax # DOS >= 3.20?
+ jae 1f # If so, okay
+ movw $bad_dos,%dx # Print error message
+ movb $0x09,%ah
+ int $0x21
+ int $0x20 # Die
+
+1:
# Compute argc and argv (assumes REGPARM)
- movl $_cmdline,%edx
pushl %eax # Make space for argv
movl %esp,%eax
calll __parse_argv
@@ -44,7 +55,7 @@ _start:
# Initialize malloc
calll __init_memory_arena
- # Now call main... (NOTE: gcc forces main to be regparm 0)
+ # Now call main
popl %eax # argc
popl %edx # argv
calll main
@@ -63,8 +74,18 @@ exit:
jmp 1b
.size exit,.-exit
+ .section ".rodata","a"
+bad_dos:
+ .ascii "Unsupported DOS version\r\n$"
+ .size bad_dos,.-bad_dos
+
.section ".bss","aw"
- .balign 4
-_cmdline:
- .space 128
- .size _cmdline,.-_cmdline
+ .balign 16
+ .globl _PSP
+_PSP:
+ .space 256
+ .size _PSP, .-_PSP
+
+ /* Purely for sanity */
+ .section ".null","a"
+ .long 0,0,0,0
diff --git a/dos/dosexe.ld b/dos/dosexe.ld
index 76bfc758..733f73d8 100644
--- a/dos/dosexe.ld
+++ b/dos/dosexe.ld
@@ -26,7 +26,7 @@ SECTIONS
__header_size = .;
__payload_lma = .;
- . = 0x100000000 - syslinux_ldlinux_size;
+ . = 0x100000000 - syslinux_size;
.payload : AT (__payload_lma) {
__payload_start = .;
*(.payload)
@@ -35,16 +35,23 @@ SECTIONS
__payload_len = ABSOLUTE(__payload_end) - ABSOLUTE(__payload_start);
__payload_dwords = __payload_len >> 2;
- __text_lma = __payload_lma + syslinux_ldlinux_size;
- __payload_sseg = (__payload_lma - __text_lma) >> 4;
- _exe_text_seg = (__text_lma - __header_size) >> 4;
+ __dgroup_lma = __payload_lma + syslinux_size;
+ __payload_sseg = (__payload_lma - __dgroup_lma) >> 4;
+ _exe_text_seg = (__dgroup_lma - __header_size) >> 4;
/*
* __assert1 = ASSERT((__payload_len == syslinux_ldlinux_size),
- * "syslinux_ldlinux_size must equal the size of .payload");
+ * "syslinux_size must equal the size of .payload");
*/
. = 0;
- .text : AT (__text_lma) {
+ __null = .;
+ .null : AT(__dgroup_lma) {
+ *(.null)
+ }
+
+ . = ALIGN(16);
+ __text_vma = .;
+ .text : AT (__text_vma + __dgroup_lma) {
*(.text .stub .text.* .gnu.linkonce.t.*)
*(.gnu.warning)
} =0x90909090
@@ -52,7 +59,7 @@ SECTIONS
. = ALIGN(16);
__rodata_vma = .;
- .rodata : AT (__rodata_vma + __text_lma) {
+ .rodata : AT (__rodata_vma + __dgroup_lma) {
*(.rodata .rodata.* .gnu.linkonce.r.*)
}
@@ -60,15 +67,15 @@ SECTIONS
data within same 128-byte chunk. */
. = ALIGN(128);
__data_vma = .;
- .data : AT (__data_vma + __text_lma) {
+ .data : AT (__data_vma + __dgroup_lma) {
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .;
- _exe_edata_low = ((_edata + __text_lma) & 511);
- _exe_edata_blocks = ((_edata + __text_lma) + 511) >> 9;
+ _exe_edata_low = ((_edata + __dgroup_lma) & 511);
+ _exe_edata_blocks = ((_edata + __dgroup_lma) + 511) >> 9;
.bss (NOLOAD) : {
__bss_start = .;
diff --git a/dos/free.c b/dos/free.c
index 020dc157..b0b72ef7 100644
--- a/dos/free.c
+++ b/dos/free.c
@@ -67,10 +67,6 @@ void free(void *ptr)
ah = (struct free_arena_header *)
((struct arena_header *)ptr - 1);
-#ifdef DEBUG_MALLOC
- assert(ah->a.type == ARENA_TYPE_USED);
-#endif
-
__free_block(ah);
/* Here we could insert code to return memory to the system. */
diff --git a/dos/getsetsl.c b/dos/getsetsl.c
index 67e954d1..fadef438 100644
--- a/dos/getsetsl.c
+++ b/dos/getsetsl.c
@@ -11,15 +11,23 @@
#include <stdlib.h>
#include "syslxint.h"
+#include "mystuff.h"
-#define __noinline __attribute__((noinline))
+static inline void *set_fs_sl(const void *p)
+{
+ uint16_t seg;
+
+ seg = ds() + ((size_t) p >> 4);
+ set_fs(seg);
+ return (void *)((size_t) p & 0xf);
+}
#if 0 /* unused */
uint8_t get_8_sl(const uint8_t * p)
{
uint8_t v;
- p = set_fs(p);
+ p = set_fs_sl(p);
asm volatile("movb %%fs:%1,%0":"=q" (v):"m"(*p));
return v;
}
@@ -29,7 +37,7 @@ uint16_t get_16_sl(const uint16_t * p)
{
uint16_t v;
- p = set_fs(p);
+ p = set_fs_sl(p);
asm volatile("movw %%fs:%1,%0":"=r" (v):"m"(*p));
return v;
}
@@ -38,7 +46,7 @@ uint32_t get_32_sl(const uint32_t * p)
{
uint32_t v;
- p = set_fs(p);
+ p = set_fs_sl(p);
asm volatile("movl %%fs:%1,%0":"=r" (v):"m"(*p));
return v;
}
@@ -47,7 +55,7 @@ uint32_t get_32_sl(const uint32_t * p)
uint64_t get_64_sl(const uint64_t * p)
{
uint32_t v0, v1;
- const uint32_t *pp = (const uint32_t *)set_fs(p);
+ const uint32_t *pp = (const uint32_t *)set_fs_sl(p);
asm volatile("movl %%fs:%1,%0" : "=r" (v0) : "m" (pp[0]));
asm volatile("movl %%fs:%1,%0" : "=r" (v1) : "m" (pp[1]));
@@ -58,26 +66,26 @@ uint64_t get_64_sl(const uint64_t * p)
#if 0 /* unused */
void set_8_sl(uint8_t * p, uint8_t v)
{
- p = set_fs(p);
+ p = set_fs_sl(p);
asm volatile("movb %1,%%fs:%0":"=m" (*p):"qi"(v));
}
#endif
void set_16_sl(uint16_t * p, uint16_t v)
{
- p = set_fs(p);
+ p = set_fs_sl(p);
asm volatile("movw %1,%%fs:%0":"=m" (*p):"ri"(v));
}
void set_32_sl(uint32_t * p, uint32_t v)
{
- p = set_fs(p);
+ p = set_fs_sl(p);
asm volatile("movl %1,%%fs:%0":"=m" (*p):"ri"(v));
}
void set_64_sl(uint64_t * p, uint64_t v)
{
- uint32_t *pp = (uint32_t *)set_fs(p);
+ uint32_t *pp = (uint32_t *)set_fs_sl(p);
asm volatile("movl %1,%%fs:%0" : "=m" (pp[0]) : "ri"((uint32_t)v));
asm volatile("movl %1,%%fs:%0" : "=m" (pp[1]) : "ri"((uint32_t)(v >> 32)));
}
diff --git a/dos/ldlinux.S b/dos/ldlinux.S
index 17a65830..9145bd7b 100644
--- a/dos/ldlinux.S
+++ b/dos/ldlinux.S
@@ -1,5 +1,5 @@
/*
- * Wrap ldlinux.sys; this needs special handling for DOS.
+ * Wrap ldlinux.sys and ldlinux.c32; this needs special handling for DOS.
*/
.section ".payload","aw"
@@ -10,6 +10,14 @@ syslinux_ldlinux:
.space ((syslinux_ldlinux - .) & 511)
syslinux_ldlinux_size = . - syslinux_ldlinux
.size syslinux_ldlinux, .-syslinux_ldlinux
+ .globl syslinux_ldlinuxc32, syslinux_ldlinuxc32_size
+syslinux_ldlinuxc32:
+ .incbin "../com32/elflink/ldlinux/ldlinux.c32"
+ .space ((syslinux_ldlinuxc32 - .) & 511)
+syslinux_ldlinuxc32_size = . - syslinux_ldlinuxc32
+ .size syslinux_ldlinuxc32, .-syslinux_ldlinuxc32
+ .globl syslinux_size
+syslinux_size = . - syslinux_ldlinux
.section ".rodata","a"
.balign 4
@@ -17,3 +25,7 @@ syslinux_ldlinux_size = . - syslinux_ldlinux
syslinux_ldlinux_len:
.long syslinux_ldlinux_size
.size syslinux_ldlinux_len, .-syslinux_ldlinux_len
+ .globl syslinux_ldlinuxc32_len
+syslinux_ldlinuxc32_len:
+ .long syslinux_ldlinuxc32_size
+ .size syslinux_ldlinuxc32_len, .-syslinux_ldlinuxc32_len
diff --git a/dos/mystuff.h b/dos/mystuff.h
index 25344413..2d9574d6 100644
--- a/dos/mystuff.h
+++ b/dos/mystuff.h
@@ -2,8 +2,7 @@
#define MYSTUFF_H
#include <inttypes.h>
-
-#define NULL ((void *)0)
+#include <stddef.h>
unsigned int skip_atou(const char **s);
unsigned int atou(const char *s);
@@ -21,4 +20,60 @@ struct diskio {
int int25_read_sector(unsigned char drive, struct diskio *dio);
int int26_write_sector(unsigned char drive, struct diskio *dio);
+struct psp {
+ uint16_t int20;
+ uint16_t nextpara;
+ uint8_t resv1;
+ uint8_t dispatcher[5];
+ uint32_t termvector;
+ uint32_t ctrlcvector;
+ uint32_t criterrvector;
+ uint16_t resv2[11];
+ uint16_t environment;
+ uint16_t resv3[23];
+ uint8_t fcb[2][16];
+ uint32_t resv4;
+ uint8_t cmdlen;
+ char cmdtail[127];
+} __attribute__((packed));
+
+extern struct psp _PSP;
+
+static inline __attribute__((const))
+uint16_t ds(void)
+{
+ uint16_t v;
+ asm("movw %%ds,%0":"=rm"(v));
+ return v;
+}
+
+static inline void set_fs(uint16_t seg)
+{
+ asm volatile("movw %0,%%fs"::"rm" (seg));
+}
+
+static inline uint8_t get_8_fs(size_t offs)
+{
+ uint8_t v;
+ asm volatile("movb %%fs:%1,%0"
+ : "=q" (v) : "m" (*(const uint8_t *)offs));
+ return v;
+}
+
+static inline uint16_t get_16_fs(size_t offs)
+{
+ uint16_t v;
+ asm volatile("movw %%fs:%1,%0"
+ : "=r" (v) : "m" (*(const uint16_t *)offs));
+ return v;
+}
+
+static inline uint32_t get_32_fs(size_t offs)
+{
+ uint32_t v;
+ asm volatile("movl %%fs:%1,%0"
+ : "=r" (v) : "m" (*(const uint32_t *)offs));
+ return v;
+}
+
#endif /* MYSTUFF_H */
diff --git a/dos/syslinux.c b/dos/syslinux.c
index fa4bf387..63a3a854 100644
--- a/dos/syslinux.c
+++ b/dos/syslinux.c
@@ -41,7 +41,7 @@ uint16_t dos_version;
void pause(void)
{
uint16_t ax;
-
+
asm volatile("int $0x16" : "=a" (ax) : "a" (0));
}
#else
@@ -121,15 +121,15 @@ int rename(const char *oldname, const char *newname)
return 0;
}
-ssize_t write_ldlinux(int fd)
+ssize_t write_file_seg(int fd, unsigned char *buf, const unsigned int len)
{
- uint16_t ldlinux_seg = ((size_t)syslinux_ldlinux >> 4) + ds();
+ uint16_t seg = ((size_t)buf >> 4) + ds();
uint32_t offset = 0;
uint16_t rv;
uint8_t err;
- while (offset < syslinux_ldlinux_len) {
- uint32_t chunk = syslinux_ldlinux_len - offset;
+ while (offset < len) {
+ uint32_t chunk = len - offset;
if (chunk > 32768)
chunk = 32768;
asm volatile ("pushw %%ds ; "
@@ -137,7 +137,7 @@ ssize_t write_ldlinux(int fd)
"int $0x21 ; "
"popw %%ds ; " "setc %0":"=bcdm" (err), "=a"(rv)
:"a"(0x4000), "b"(fd), "c"(chunk), "d" (offset & 15),
- "SD" ((uint16_t)(ldlinux_seg + (offset >> 4))));
+ "SD" ((uint16_t)(seg + (offset >> 4))));
if (err || rv == 0)
die("file write error");
offset += rv;
@@ -187,7 +187,7 @@ void write_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
dio.sectors = nsecs;
dio.bufoffs = (uintptr_t) buf;
dio.bufseg = data_segment();
-
+
if (dos_version >= 0x070a) {
/* Try FAT32-aware system call first */
asm volatile("int $0x21 ; jc 1f ; xorw %0,%0\n"
@@ -220,7 +220,7 @@ void read_device(int drive, void *buf, size_t nsecs, unsigned int sector)
dio.sectors = nsecs;
dio.bufoffs = (uintptr_t) buf;
dio.bufseg = data_segment();
-
+
if (dos_version >= 0x070a) {
/* Try FAT32-aware system call first */
asm volatile("int $0x21 ; jc 1f ; xorw %0,%0\n"
@@ -402,14 +402,6 @@ int libfat_xpread(intptr_t pp, void *buf, size_t secsize,
static inline void get_dos_version(void)
{
- uint16_t ver;
-
- asm("int $0x21 ; xchgb %%ah,%%al"
- : "=a" (ver)
- : "a" (0x3001)
- : "ebx", "ecx");
- dos_version = ver;
-
dprintf("DOS version %d.%d\n", (dos_version >> 8), dos_version & 0xff);
}
@@ -475,7 +467,7 @@ soft_fail:
if (hard_lock) {
/* Hard locking, only level 4 supported */
/* This is needed for Win9x in DOS mode */
-
+
err = do_lock(4);
if (err) {
if (err == 0x0001) {
@@ -583,11 +575,53 @@ static void adjust_mbr(int device, int writembr, int set_active)
write_mbr(device, sectbuf);
}
+static void move_file(int dev_fd, char *pathname, char *filename)
+{
+ char new_name[160];
+ char *cp = new_name + 3;
+ const char *sd;
+ int slash = 1;
+
+ new_name[0] = dev_fd | 0x40;
+ new_name[1] = ':';
+ new_name[2] = '\\';
+
+ for (sd = opt.directory; *sd; sd++) {
+ char c = *sd;
+
+ if (c == '/' || c == '\\') {
+ if (slash)
+ continue;
+ c = '\\';
+ slash = 1;
+ } else {
+ slash = 0;
+ }
+
+ *cp++ = c;
+ }
+
+ /* Skip if subdirectory == root */
+ if (cp > new_name + 3) {
+ if (!slash)
+ *cp++ = '\\';
+
+ memcpy(cp, filename, 12);
+
+ set_attributes(pathname, 0);
+ if (rename(pathname, new_name))
+ set_attributes(pathname, 0x07);
+ else
+ set_attributes(new_name, 0x07);
+ }
+}
+
int main(int argc, char *argv[])
{
static unsigned char sectbuf[SECTOR_SIZE];
int dev_fd, fd;
static char ldlinux_name[] = "@:\\ldlinux.sys";
+ static char ldlinuxc32_name[] = "@:\\ldlinux.c32";
struct libfat_filesystem *fs;
libfat_sector_t s, *secp;
libfat_sector_t *sectors;
@@ -647,14 +681,21 @@ int main(int argc, char *argv[])
}
ldlinux_name[0] = dev_fd | 0x40;
+ ldlinuxc32_name[0] = dev_fd | 0x40;
set_attributes(ldlinux_name, 0);
fd = creat(ldlinux_name, 0); /* SYSTEM HIDDEN READONLY */
- write_ldlinux(fd);
+ write_file_seg(fd, syslinux_ldlinux, syslinux_ldlinux_len);
write_file(fd, syslinux_adv, 2 * ADV_SIZE);
close(fd);
set_attributes(ldlinux_name, 0x07); /* SYSTEM HIDDEN READONLY */
+ set_attributes(ldlinuxc32_name, 0);
+ fd = creat(ldlinuxc32_name, 0); /* SYSTEM HIDDEN READONLY */
+ write_file_seg(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len);
+ close(fd);
+ set_attributes(ldlinuxc32_name, 0x07); /* SYSTEM HIDDEN READONLY */
+
/*
* Now, use libfat to create a block map. This probably
* should be changed to use ioctl(...,FIBMAP,...) since
@@ -681,43 +722,8 @@ int main(int argc, char *argv[])
* If requested, move ldlinux.sys
*/
if (opt.directory) {
- char new_ldlinux_name[160];
- char *cp = new_ldlinux_name + 3;
- const char *sd;
- int slash = 1;
-
- new_ldlinux_name[0] = dev_fd | 0x40;
- new_ldlinux_name[1] = ':';
- new_ldlinux_name[2] = '\\';
-
- for (sd = opt.directory; *sd; sd++) {
- char c = *sd;
-
- if (c == '/' || c == '\\') {
- if (slash)
- continue;
- c = '\\';
- slash = 1;
- } else {
- slash = 0;
- }
-
- *cp++ = c;
- }
-
- /* Skip if subdirectory == root */
- if (cp > new_ldlinux_name + 3) {
- if (!slash)
- *cp++ = '\\';
-
- memcpy(cp, "ldlinux.sys", 12);
-
- set_attributes(ldlinux_name, 0);
- if (rename(ldlinux_name, new_ldlinux_name))
- set_attributes(ldlinux_name, 0x07);
- else
- set_attributes(new_ldlinux_name, 0x07);
- }
+ move_file(dev_fd, ldlinux_name, "ldlinux.sys");
+ move_file(dev_fd, ldlinuxc32_name, "ldlinux.c32");
}
/*
diff --git a/extlinux/Makefile b/extlinux/Makefile
index 6cde574e..f20a71db 100644
--- a/extlinux/Makefile
+++ b/extlinux/Makefile
@@ -32,6 +32,7 @@ SRCS = main.c \
../libinstaller/setadv.c \
../libinstaller/advio.c \
../libinstaller/bootsect_bin.c \
+ ../libinstaller/ldlinuxc32_bin.c \
../libinstaller/ldlinux_bin.c
OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS)))
diff --git a/extlinux/main.c b/extlinux/main.c
index dbf538a1..aa20e1bd 100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -14,7 +14,8 @@
/*
* extlinux.c
*
- * Install the syslinux boot block on an fat, ntfs, ext2/3/4 and btrfs filesystem
+ * Install the syslinux boot block on an fat, ntfs, ext2/3/4, btrfs and xfs
+ * filesystem.
*/
#define _GNU_SOURCE /* Enable everything */
@@ -46,6 +47,10 @@
#include "btrfs.h"
#include "fat.h"
#include "ntfs.h"
+#include "xfs.h"
+#include "xfs_types.h"
+#include "xfs_sb.h"
+#include "misc.h"
#include "../version.h"
#include "syslxint.h"
#include "syslxcom.h" /* common functions shared with extlinux and syslinux */
@@ -64,6 +69,13 @@
#define EXT2_SUPER_OFFSET 1024
#endif
+/* Since we have unused 2048 bytes in the primary AG of an XFS partition,
+ * we will use the first 0~512 bytes starting from 2048 for the Syslinux
+ * boot sector.
+ */
+#define XFS_BOOTSECT_OFFSET (4 << SECTOR_SHIFT)
+#define XFS_SUPPORTED_BLOCKSIZE 4096 /* 4 KiB filesystem block size */
+
/* the btrfs partition first 64K blank area is used to store boot sector and
boot image, the boot sector is from 0~512, the boot image starts after */
#define BTRFS_BOOTSECT_AREA 65536
@@ -295,7 +307,8 @@ static int patch_file_and_bootblock(int fd, const char *dir, int devfd)
nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
nsect += 2; /* Two sectors for the ADV */
sectp = alloca(sizeof(sector_t) * nsect);
- if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) {
+ if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS ||
+ fs_type == XFS) {
if (sectmap(fd, sectp, nsect)) {
perror("bmap");
exit(1);
@@ -328,6 +341,7 @@ int install_bootblock(int fd, const char *device)
struct btrfs_super_block sb2;
struct fat_boot_sector sb3;
struct ntfs_boot_sector sb4;
+ xfs_sb_t sb5;
bool ok = false;
if (fs_type == EXT2) {
@@ -335,6 +349,7 @@ int install_bootblock(int fd, const char *device)
perror("reading superblock");
return 1;
}
+
if (sb.s_magic == EXT2_SUPER_MAGIC)
ok = true;
} else if (fs_type == BTRFS) {
@@ -350,6 +365,7 @@ int install_bootblock(int fd, const char *device)
perror("reading fat superblock");
return 1;
}
+
if (fat_check_sb_fields(&sb3))
ok = true;
} else if (fs_type == NTFS) {
@@ -360,12 +376,34 @@ int install_bootblock(int fd, const char *device)
if (ntfs_check_sb_fields(&sb4))
ok = true;
+ } else if (fs_type == XFS) {
+ if (xpread(fd, &sb5, sizeof sb5, 0) != sizeof sb5) {
+ perror("reading xfs superblock");
+ return 1;
+ }
+
+ if (sb5.sb_magicnum == *(u32 *)XFS_SB_MAGIC) {
+ if (be32_to_cpu(sb5.sb_blocksize) != XFS_SUPPORTED_BLOCKSIZE) {
+ fprintf(stderr,
+ "You need to have 4 KiB filesystem block size for "
+ " being able to install Syslinux in your XFS "
+ "partition (because there is no enough space in MBR to "
+ "determine where Syslinux bootsector can be installed "
+ "regardless the filesystem block size)\n");
+ return 1;
+ }
+
+ ok = true;
+ }
}
+
if (!ok) {
- fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n",
- device);
+ fprintf(stderr,
+ "no fat, ntfs, ext2/3/4, btrfs or xfs superblock found on %s\n",
+ device);
return 1;
}
+
if (fs_type == VFAT) {
struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect;
if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen ||
@@ -385,6 +423,12 @@ int install_bootblock(int fd, const char *device)
perror("writing ntfs bootblock");
return 1;
}
+ } else if (fs_type == XFS) {
+ if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len,
+ XFS_BOOTSECT_OFFSET) != syslinux_bootsect_len) {
+ perror("writing xfs bootblock");
+ return 1;
+ }
} else {
if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0)
!= syslinux_bootsect_len) {
@@ -396,18 +440,66 @@ int install_bootblock(int fd, const char *device)
return 0;
}
+static int rewrite_boot_image(int devfd, const char *path, const char *filename)
+{
+ int fd;
+ int ret;
+ int modbytes;
+
+ /* Let's create LDLINUX.SYS file again (if it already exists, of course) */
+ fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ perror(filename);
+ return -1;
+ }
+
+ /* Write boot image data into LDLINUX.SYS file */
+ ret = xpwrite(fd, boot_image, boot_image_len, 0);
+ if (ret != boot_image_len) {
+ perror("writing bootblock");
+ goto error;
+ }
+
+ /* Write ADV */
+ ret = xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, boot_image_len);
+ if (ret != 2 * ADV_SIZE) {
+ fprintf(stderr, "%s: write failure on %s\n", program, filename);
+ goto error;
+ }
+
+ /* Map the file, and patch the initial sector accordingly */
+ modbytes = patch_file_and_bootblock(fd, path, devfd);
+
+ /* Write the patch area again - this relies on the file being overwritten
+ * in place! */
+ ret = xpwrite(fd, boot_image, modbytes, 0);
+ if (ret != modbytes) {
+ fprintf(stderr, "%s: write failure on %s\n", program, filename);
+ goto error;
+ }
+
+ return fd;
+
+error:
+ close(fd);
+
+ return -1;
+}
+
int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
{
- char *file, *oldfile;
+ char *file, *oldfile, *c32file;
int fd = -1, dirfd = -1;
- int modbytes;
- int r1, r2;
+ int r1, r2, r3;
r1 = asprintf(&file, "%s%sldlinux.sys",
path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
r2 = asprintf(&oldfile, "%s%sextlinux.sys",
path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
- if (r1 < 0 || !file || r2 < 0 || !oldfile) {
+ r3 = asprintf(&c32file, "%s%sldlinux.c32",
+ path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
+ if (r1 < 0 || !file || r2 < 0 || !oldfile || r3 < 0 || !c32file) {
perror(program);
return 1;
}
@@ -429,30 +521,9 @@ int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
}
close(fd);
- fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
- S_IRUSR | S_IRGRP | S_IROTH);
- if (fd < 0) {
- perror(file);
+ fd = rewrite_boot_image(devfd, path, file);
+ if (fd < 0)
goto bail;
- }
-
- /* Write it the first time */
- if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
- xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
- boot_image_len) != 2 * ADV_SIZE) {
- fprintf(stderr, "%s: write failure on %s\n", program, file);
- goto bail;
- }
-
- /* Map the file, and patch the initial sector accordingly */
- modbytes = patch_file_and_bootblock(fd, path, devfd);
-
- /* Write the patch area again - this relies on the file being
- overwritten in place! */
- if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
- fprintf(stderr, "%s: write failure on %s\n", program, file);
- goto bail;
- }
/* Attempt to set immutable flag and remove all write access */
/* Only set immutable flag if file is owned by root */
@@ -474,8 +545,22 @@ int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
unlink(oldfile);
}
+ fd = open(c32file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ perror(c32file);
+ goto bail;
+ }
+
+ r3 = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0);
+ if (r3 != syslinux_ldlinuxc32_len) {
+ fprintf(stderr, "%s: write failure on %s\n", program, c32file);
+ goto bail;
+ }
+
free(file);
free(oldfile);
+ free(c32file);
return 0;
bail:
@@ -486,6 +571,7 @@ bail:
free(file);
free(oldfile);
+ free(c32file);
return 1;
}
@@ -494,6 +580,9 @@ bail:
since the cow feature of btrfs will move the ldlinux.sys every where */
int btrfs_install_file(const char *path, int devfd, struct stat *rst)
{
+ char *file;
+ int fd, rv;
+
patch_file_and_bootblock(-1, path, devfd);
if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
!= boot_image_len) {
@@ -511,7 +600,125 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst)
perror(path);
return 1;
}
+
+ /*
+ * Note that we *can* install ldinux.c32 as a regular file because
+ * it doesn't need to be within the first 64K. The Syslinux core
+ * has enough smarts to search the btrfs dirs and find this file.
+ */
+ rv = asprintf(&file, "%s%sldlinux.c32",
+ path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
+ if (rv < 0 || !file) {
+ perror(program);
+ return 1;
+ }
+
+ fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ perror(file);
+ free(file);
+ return 1;
+ }
+
+ rv = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0);
+ if (rv != (int)syslinux_ldlinuxc32_len) {
+ fprintf(stderr, "%s: write failure on %s\n", program, file);
+ rv = 1;
+ } else
+ rv = 0;
+
+ close(fd);
+ free(file);
+ return rv;
+}
+
+/*
+ * Due to historical reasons (SGI IRIX's design of disk layouts), the first
+ * sector in the primary AG on XFS filesystems contains the superblock, which is
+ * a problem with bootloaders that rely on BIOSes (that load VBRs which are
+ * (located in the first sector of the partition).
+ *
+ * Thus, we need to handle this issue, otherwise Syslinux will damage the XFS's
+ * superblock.
+ */
+static int xfs_install_file(const char *path, int devfd, struct stat *rst)
+{
+ static char file[PATH_MAX + 1];
+ static char c32file[PATH_MAX + 1];
+ int dirfd = -1;
+ int fd = -1;
+ int retval;
+
+ snprintf(file, PATH_MAX + 1, "%s%sldlinux.sys", path,
+ path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
+ snprintf(c32file, PATH_MAX + 1, "%s%sldlinux.c32", path,
+ path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
+
+ dirfd = open(path, O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0) {
+ perror(path);
+ goto bail;
+ }
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ if (errno != ENOENT) {
+ perror(file);
+ goto bail;
+ }
+ } else {
+ clear_attributes(fd);
+ }
+
+ close(fd);
+
+ fd = rewrite_boot_image(devfd, path, file);
+ if (fd < 0)
+ goto bail;
+
+ /* Attempt to set immutable flag and remove all write access */
+ /* Only set immutable flag if file is owned by root */
+ set_attributes(fd);
+
+ if (fstat(fd, rst)) {
+ perror(file);
+ goto bail;
+ }
+
+ close(dirfd);
+ close(fd);
+
+ dirfd = -1;
+ fd = -1;
+
+ fd = open(c32file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ perror(c32file);
+ goto bail;
+ }
+
+ retval = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0);
+ if (retval != (int)syslinux_ldlinuxc32_len) {
+ fprintf(stderr, "%s: write failure on %s\n", program, file);
+ goto bail;
+ }
+
+ close(fd);
+
+ sync();
+
return 0;
+
+bail:
+ if (dirfd >= 0)
+ close(dirfd);
+
+ if (fd >= 0)
+ close(fd);
+
+ return 1;
}
/*
@@ -748,11 +955,14 @@ static char * get_default_subvol(char * rootdir, char * subvol)
static int install_file(const char *path, int devfd, struct stat *rst)
{
- if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
- return ext2_fat_install_file(path, devfd, rst);
- else if (fs_type == BTRFS)
- return btrfs_install_file(path, devfd, rst);
- return 1;
+ if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
+ return ext2_fat_install_file(path, devfd, rst);
+ else if (fs_type == BTRFS)
+ return btrfs_install_file(path, devfd, rst);
+ else if (fs_type == XFS)
+ return xfs_install_file(path, devfd, rst);
+
+ return 1;
}
#ifdef __KLIBC__
@@ -847,14 +1057,22 @@ static const char *find_device(const char *mtab_file, dev_t dev)
}
break;
+ case XFS:
+ if (!strcmp(mnt->mnt_type, "xfs") && !stat(mnt->mnt_fsname, &dst) &&
+ dst.st_rdev == dev) {
+ done = true;
+ break;
+ }
case NONE:
break;
}
+
if (done) {
devname = strdup(mnt->mnt_fsname);
break;
}
}
+
endmntent(mtab);
return devname;
@@ -915,6 +1133,8 @@ static const char *find_device_mountinfo(const char *path, dev_t dev)
struct stat st;
m = find_mount(path, NULL);
+ if (!m)
+ return NULL;
if (m->devpath[0] == '/' && m->dev == dev &&
!stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
@@ -1098,6 +1318,7 @@ static int open_device(const char *path, struct stat *st, const char **_devname)
fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
return -1;
}
+
if (sfs.f_type == EXT2_SUPER_MAGIC)
fs_type = EXT2;
else if (sfs.f_type == BTRFS_SUPER_MAGIC)
@@ -1106,10 +1327,13 @@ static int open_device(const char *path, struct stat *st, const char **_devname)
fs_type = VFAT;
else if (sfs.f_type == NTFS_SB_MAGIC ||
sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */)
- fs_type = NTFS;
+ fs_type = NTFS;
+ else if (sfs.f_type == XFS_SUPER_MAGIC)
+ fs_type = XFS;
if (!fs_type) {
- fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n",
+ fprintf(stderr,
+ "%s: not a fat, ntfs, ext2/3/4, btrfs or xfs filesystem: %s\n",
program, path);
return -1;
}
@@ -1143,6 +1367,16 @@ static int btrfs_read_adv(int devfd)
return syslinux_validate_adv(syslinux_adv) ? 1 : 0;
}
+static inline int xfs_read_adv(int devfd)
+{
+ const size_t adv_size = 2 * ADV_SIZE;
+
+ if (xpread(devfd, syslinux_adv, adv_size, boot_image_len) != adv_size)
+ return -1;
+
+ return syslinux_validate_adv(syslinux_adv) ? 1 : 0;
+}
+
static int ext_read_adv(const char *path, int devfd, const char **namep)
{
int err;
@@ -1151,6 +1385,9 @@ static int ext_read_adv(const char *path, int devfd, const char **namep)
if (fs_type == BTRFS) {
/* btrfs "ldlinux.sys" is in 64k blank area */
return btrfs_read_adv(devfd);
+ } else if (fs_type == XFS) {
+ /* XFS "ldlinux.sys" is in the first 2048 bytes of the primary AG */
+ return xfs_read_adv(devfd);
} else {
err = read_adv(path, name = "ldlinux.sys");
if (err == 2) /* ldlinux.sys does not exist */
diff --git a/extlinux/misc.h b/extlinux/misc.h
new file mode 100644
index 00000000..7f2f1b33
--- /dev/null
+++ b/extlinux/misc.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MISC_H_
+#define MISC_H_
+
+/* Return a 64-bit litte-endian value from a given 64-bit big-endian one */
+static inline uint64_t be64_to_cpu(uint64_t val)
+{
+ return (uint64_t)((((uint64_t)val & (uint64_t)0x00000000000000ffULL) << 56) |
+ (((uint64_t)val & (uint64_t)0x000000000000ff00ULL) << 40) |
+ (((uint64_t)val & (uint64_t)0x0000000000ff0000ULL) << 24) |
+ (((uint64_t)val & (uint64_t)0x00000000ff000000ULL) << 8) |
+ (((uint64_t)val & (uint64_t)0x000000ff00000000ULL) >> 8) |
+ (((uint64_t)val & (uint64_t)0x0000ff0000000000ULL) >> 24) |
+ (((uint64_t)val & (uint64_t)0x00ff000000000000ULL) >> 40) |
+ (((uint64_t)val & (uint64_t)0xff00000000000000ULL) >> 56));
+}
+
+/* Return a 32-bit litte-endian value from a given 32-bit big-endian one */
+static inline uint32_t be32_to_cpu(uint32_t val)
+{
+ return (uint32_t)((((uint32_t)val & (uint32_t)0x000000ffUL) << 24) |
+ (((uint32_t)val & (uint32_t)0x0000ff00UL) << 8) |
+ (((uint32_t)val & (uint32_t)0x00ff0000UL) >> 8) |
+ (((uint32_t)val & (uint32_t)0xff000000UL) >> 24));
+}
+
+/* Return a 16-bit litte-endian value from a given 16-bit big-endian one */
+static inline uint16_t be16_to_cpu(uint16_t val)
+{
+ return (uint16_t)((((uint16_t)val & (uint16_t)0x00ffU) << 8) |
+ (((uint16_t)val & (uint16_t)0xff00U) >> 8));
+}
+
+#endif /* MISC_H_ */
diff --git a/extlinux/xfs.h b/extlinux/xfs.h
new file mode 100644
index 00000000..412c2662
--- /dev/null
+++ b/extlinux/xfs.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef XFS_H_
+#define XFS_H_
+
+#define XFS_SUPER_MAGIC 0x58465342
+
+#endif /* XFS_H_ */
diff --git a/extlinux/xfs_fs.h b/extlinux/xfs_fs.h
new file mode 100644
index 00000000..587820ec
--- /dev/null
+++ b/extlinux/xfs_fs.h
@@ -0,0 +1,501 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 1995-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef XFS_FS_H_
+#define XFS_FS_H_
+
+/*
+ * SGI's XFS filesystem's major stuff (constants, structures)
+ */
+
+/*
+ * Direct I/O attribute record used with XFS_IOC_DIOINFO
+ * d_miniosz is the min xfer size, xfer size multiple and file seek offset
+ * alignment.
+ */
+struct dioattr {
+ uint32_t d_mem; /* data buffer memory alignment */
+ uint32_t d_miniosz; /* min xfer size */
+ uint32_t d_maxiosz; /* max xfer size */
+};
+
+/*
+ * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR.
+ */
+struct fsxattr {
+ uint32_t fsx_xflags; /* xflags field value (get/set) */
+ uint32_t fsx_extsize; /* extsize field value (get/set)*/
+ uint32_t fsx_nextents; /* nextents field value (get) */
+ uint32_t fsx_projid; /* project identifier (get/set) */
+ unsigned char fsx_pad[12];
+};
+
+/*
+ * Flags for the bs_xflags/fsx_xflags field
+ * There should be a one-to-one correspondence between these flags and the
+ * XFS_DIFLAG_s.
+ */
+#define XFS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */
+#define XFS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */
+#define XFS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */
+#define XFS_XFLAG_APPEND 0x00000010 /* all writes append */
+#define XFS_XFLAG_SYNC 0x00000020 /* all writes synchronous */
+#define XFS_XFLAG_NOATIME 0x00000040 /* do not update access time */
+#define XFS_XFLAG_NODUMP 0x00000080 /* do not include in backups */
+#define XFS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */
+#define XFS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */
+#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
+#define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */
+#define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */
+#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */
+#define XFS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */
+#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
+
+/*
+ * Structure for XFS_IOC_GETBMAP.
+ * On input, fill in bmv_offset and bmv_length of the first structure
+ * to indicate the area of interest in the file, and bmv_entries with
+ * the number of array elements given back. The first structure is
+ * updated on return to give the offset and length for the next call.
+ */
+struct getbmap {
+ int64_t bmv_offset; /* file offset of segment in blocks */
+ int64_t bmv_block; /* starting block (64-bit daddr_t) */
+ int64_t bmv_length; /* length of segment, blocks */
+ int32_t bmv_count; /* # of entries in array incl. 1st */
+ int32_t bmv_entries; /* # of entries filled in (output) */
+};
+
+/*
+ * Structure for XFS_IOC_GETBMAPX. Fields bmv_offset through bmv_entries
+ * are used exactly as in the getbmap structure. The getbmapx structure
+ * has additional bmv_iflags and bmv_oflags fields. The bmv_iflags field
+ * is only used for the first structure. It contains input flags
+ * specifying XFS_IOC_GETBMAPX actions. The bmv_oflags field is filled
+ * in by the XFS_IOC_GETBMAPX command for each returned structure after
+ * the first.
+ */
+struct getbmapx {
+ int64_t bmv_offset; /* file offset of segment in blocks */
+ int64_t bmv_block; /* starting block (64-bit daddr_t) */
+ int64_t bmv_length; /* length of segment, blocks */
+ int32_t bmv_count; /* # of entries in array incl. 1st */
+ int32_t bmv_entries; /* # of entries filled in (output). */
+ int32_t bmv_iflags; /* input flags (1st structure) */
+ int32_t bmv_oflags; /* output flags (after 1st structure)*/
+ int32_t bmv_unused1; /* future use */
+ int32_t bmv_unused2; /* future use */
+};
+
+/* bmv_iflags values - set by XFS_IOC_GETBMAPX caller. */
+#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */
+#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */
+#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */
+#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */
+#define BMV_IF_NO_HOLES 0x10 /* Do not return holes */
+#define BMV_IF_VALID \
+ (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC| \
+ BMV_IF_DELALLOC|BMV_IF_NO_HOLES)
+
+/* bmv_oflags values - returned for each non-header segment */
+#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
+#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */
+#define BMV_OF_LAST 0x4 /* segment is the last in the file */
+
+/*
+ * Structure for XFS_IOC_FSSETDM.
+ * For use by backup and restore programs to set the XFS on-disk inode
+ * fields di_dmevmask and di_dmstate. These must be set to exactly and
+ * only values previously obtained via xfs_bulkstat! (Specifically the
+ * xfs_bstat_t fields bs_dmevmask and bs_dmstate.)
+ */
+struct fsdmidata {
+ uint32_t fsd_dmevmask; /* corresponds to di_dmevmask */
+ __u16 fsd_padding;
+ __u16 fsd_dmstate; /* corresponds to di_dmstate */
+};
+
+/*
+ * File segment locking set data type for 64 bit access.
+ * Also used for all the RESV/FREE interfaces.
+ */
+typedef struct xfs_flock64 {
+ __s16 l_type;
+ __s16 l_whence;
+ int64_t l_start;
+ int64_t l_len; /* len == 0 means until end of file */
+ int32_t l_sysid;
+ uint32_t l_pid;
+ int32_t l_pad[4]; /* reserve area */
+} xfs_flock64_t;
+
+/*
+ * Output for XFS_IOC_FSGEOMETRY_V1
+ */
+typedef struct xfs_fsop_geom_v1 {
+ uint32_t blocksize; /* filesystem (data) block size */
+ uint32_t rtextsize; /* realtime extent size */
+ uint32_t agblocks; /* fsblocks in an AG */
+ uint32_t agcount; /* number of allocation groups */
+ uint32_t logblocks; /* fsblocks in the log */
+ uint32_t sectsize; /* (data) sector size, bytes */
+ uint32_t inodesize; /* inode size in bytes */
+ uint32_t imaxpct; /* max allowed inode space(%) */
+ uint64_t datablocks; /* fsblocks in data subvolume */
+ uint64_t rtblocks; /* fsblocks in realtime subvol */
+ uint64_t rtextents; /* rt extents in realtime subvol*/
+ uint64_t logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ uint32_t sunit; /* stripe unit, fsblocks */
+ uint32_t swidth; /* stripe width, fsblocks */
+ int32_t version; /* structure version */
+ uint32_t flags; /* superblock version flags */
+ uint32_t logsectsize; /* log sector size, bytes */
+ uint32_t rtsectsize; /* realtime sector size, bytes */
+ uint32_t dirblocksize; /* directory block size, bytes */
+} xfs_fsop_geom_v1_t;
+
+/*
+ * Output for XFS_IOC_FSGEOMETRY
+ */
+typedef struct xfs_fsop_geom {
+ uint32_t blocksize; /* filesystem (data) block size */
+ uint32_t rtextsize; /* realtime extent size */
+ uint32_t agblocks; /* fsblocks in an AG */
+ uint32_t agcount; /* number of allocation groups */
+ uint32_t logblocks; /* fsblocks in the log */
+ uint32_t sectsize; /* (data) sector size, bytes */
+ uint32_t inodesize; /* inode size in bytes */
+ uint32_t imaxpct; /* max allowed inode space(%) */
+ uint64_t datablocks; /* fsblocks in data subvolume */
+ uint64_t rtblocks; /* fsblocks in realtime subvol */
+ uint64_t rtextents; /* rt extents in realtime subvol*/
+ uint64_t logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ uint32_t sunit; /* stripe unit, fsblocks */
+ uint32_t swidth; /* stripe width, fsblocks */
+ int32_t version; /* structure version */
+ uint32_t flags; /* superblock version flags */
+ uint32_t logsectsize; /* log sector size, bytes */
+ uint32_t rtsectsize; /* realtime sector size, bytes */
+ uint32_t dirblocksize; /* directory block size, bytes */
+ uint32_t logsunit; /* log stripe unit, bytes */
+} xfs_fsop_geom_t;
+
+/* Output for XFS_FS_COUNTS */
+typedef struct xfs_fsop_counts {
+ uint64_t freedata; /* free data section blocks */
+ uint64_t freertx; /* free rt extents */
+ uint64_t freeino; /* free inodes */
+ uint64_t allocino; /* total allocated inodes */
+} xfs_fsop_counts_t;
+
+/* Input/Output for XFS_GET_RESBLKS and XFS_SET_RESBLKS */
+typedef struct xfs_fsop_resblks {
+ uint64_t resblks;
+ uint64_t resblks_avail;
+} xfs_fsop_resblks_t;
+
+#define XFS_FSOP_GEOM_VERSION 0
+
+#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */
+#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */
+#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */
+#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */
+#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */
+#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */
+#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */
+#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */
+#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */
+#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */
+#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */
+
+
+/*
+ * Minimum and maximum sizes need for growth checks
+ */
+#define XFS_MIN_AG_BLOCKS 64
+#define XFS_MIN_LOG_BLOCKS 512ULL
+#define XFS_MAX_LOG_BLOCKS (1024 * 1024ULL)
+#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024ULL)
+
+/* keep the maximum size under 2^31 by a small amount */
+#define XFS_MAX_LOG_BYTES \
+ ((2 * 1024 * 1024 * 1024ULL) - XFS_MIN_LOG_BYTES)
+
+/* Used for sanity checks on superblock */
+#define XFS_MAX_DBLOCKS(s) ((xfs_drfsbno_t)(s)->sb_agcount * (s)->sb_agblocks)
+#define XFS_MIN_DBLOCKS(s) ((xfs_drfsbno_t)((s)->sb_agcount - 1) * \
+ (s)->sb_agblocks + XFS_MIN_AG_BLOCKS)
+
+/*
+ * Structures for XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG & XFS_IOC_FSGROWFSRT
+ */
+typedef struct xfs_growfs_data {
+ uint64_t newblocks; /* new data subvol size, fsblocks */
+ uint32_t imaxpct; /* new inode space percentage limit */
+} xfs_growfs_data_t;
+
+typedef struct xfs_growfs_log {
+ uint32_t newblocks; /* new log size, fsblocks */
+ uint32_t isint; /* 1 if new log is internal */
+} xfs_growfs_log_t;
+
+typedef struct xfs_growfs_rt {
+ uint64_t newblocks; /* new realtime size, fsblocks */
+ uint32_t extsize; /* new realtime extent size, fsblocks */
+} xfs_growfs_rt_t;
+
+
+/*
+ * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE
+ */
+typedef struct xfs_bstime {
+ time_t tv_sec; /* seconds */
+ int32_t tv_nsec; /* and nanoseconds */
+} xfs_bstime_t;
+
+typedef struct xfs_bstat {
+ uint64_t bs_ino; /* inode number */
+ __u16 bs_mode; /* type and mode */
+ __u16 bs_nlink; /* number of links */
+ uint32_t bs_uid; /* user id */
+ uint32_t bs_gid; /* group id */
+ uint32_t bs_rdev; /* device value */
+ int32_t bs_blksize; /* block size */
+ int64_t bs_size; /* file size */
+ xfs_bstime_t bs_atime; /* access time */
+ xfs_bstime_t bs_mtime; /* modify time */
+ xfs_bstime_t bs_ctime; /* inode change time */
+ int64_t bs_blocks; /* number of blocks */
+ uint32_t bs_xflags; /* extended flags */
+ int32_t bs_extsize; /* extent size */
+ int32_t bs_extents; /* number of extents */
+ uint32_t bs_gen; /* generation count */
+ __u16 bs_projid_lo; /* lower part of project id */
+#define bs_projid bs_projid_lo /* (previously just bs_projid) */
+ __u16 bs_forkoff; /* inode fork offset in bytes */
+ __u16 bs_projid_hi; /* higher part of project id */
+ unsigned char bs_pad[10]; /* pad space, unused */
+ uint32_t bs_dmevmask; /* DMIG event mask */
+ __u16 bs_dmstate; /* DMIG state info */
+ __u16 bs_aextents; /* attribute number of extents */
+} xfs_bstat_t;
+
+/*
+ * The user-level BulkStat Request interface structure.
+ */
+typedef struct xfs_fsop_bulkreq {
+ uint64_t __user *lastip; /* last inode # pointer */
+ int32_t icount; /* count of entries in buffer */
+ void __user *ubuffer;/* user buffer for inode desc. */
+ int32_t __user *ocount; /* output count pointer */
+} xfs_fsop_bulkreq_t;
+
+
+/*
+ * Structures returned from xfs_inumbers routine (XFS_IOC_FSINUMBERS).
+ */
+typedef struct xfs_inogrp {
+ uint64_t xi_startino; /* starting inode number */
+ int32_t xi_alloccount; /* # bits set in allocmask */
+ uint64_t xi_allocmask; /* mask of allocated inodes */
+} xfs_inogrp_t;
+
+
+/*
+ * Error injection.
+ */
+typedef struct xfs_error_injection {
+ int32_t fd;
+ int32_t errtag;
+} xfs_error_injection_t;
+
+
+/*
+ * The user-level Handle Request interface structure.
+ */
+typedef struct xfs_fsop_handlereq {
+ uint32_t fd; /* fd for FD_TO_HANDLE */
+ void __user *path; /* user pathname */
+ uint32_t oflags; /* open flags */
+ void __user *ihandle;/* user supplied handle */
+ uint32_t ihandlen; /* user supplied length */
+ void __user *ohandle;/* user buffer for handle */
+ uint32_t __user *ohandlen;/* user buffer length */
+} xfs_fsop_handlereq_t;
+
+/*
+ * Compound structures for passing args through Handle Request interfaces
+ * xfs_fssetdm_by_handle, xfs_attrlist_by_handle, xfs_attrmulti_by_handle
+ * - ioctls: XFS_IOC_FSSETDM_BY_HANDLE, XFS_IOC_ATTRLIST_BY_HANDLE, and
+ * XFS_IOC_ATTRMULTI_BY_HANDLE
+ */
+
+typedef struct xfs_fsop_setdm_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle information */
+ struct fsdmidata __user *data; /* DMAPI data */
+} xfs_fsop_setdm_handlereq_t;
+
+typedef struct xfs_attrlist_cursor {
+ uint32_t opaque[4];
+} xfs_attrlist_cursor_t;
+
+typedef struct xfs_fsop_attrlist_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle interface structure */
+ struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */
+ uint32_t flags; /* which namespace to use */
+ uint32_t buflen; /* length of buffer supplied */
+ void __user *buffer; /* returned names */
+} xfs_fsop_attrlist_handlereq_t;
+
+typedef struct xfs_attr_multiop {
+ uint32_t am_opcode;
+#define ATTR_OP_GET 1 /* return the indicated attr's value */
+#define ATTR_OP_SET 2 /* set/create the indicated attr/value pair */
+#define ATTR_OP_REMOVE 3 /* remove the indicated attr */
+ int32_t am_error;
+ void __user *am_attrname;
+ void __user *am_attrvalue;
+ uint32_t am_length;
+ uint32_t am_flags;
+} xfs_attr_multiop_t;
+
+typedef struct xfs_fsop_attrmulti_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle interface structure */
+ uint32_t opcount;/* count of following multiop */
+ struct xfs_attr_multiop __user *ops; /* attr_multi data */
+} xfs_fsop_attrmulti_handlereq_t;
+
+/*
+ * per machine unique filesystem identifier types.
+ */
+typedef struct { uint32_t val[2]; } xfs_fsid_t; /* file system id type */
+
+typedef struct xfs_fid {
+ __u16 fid_len; /* length of remainder */
+ __u16 fid_pad;
+ uint32_t fid_gen; /* generation number */
+ uint64_t fid_ino; /* 64 bits inode number */
+} xfs_fid_t;
+
+typedef struct xfs_handle {
+ union {
+ int64_t align; /* force alignment of ha_fid */
+ xfs_fsid_t _ha_fsid; /* unique file system identifier */
+ } ha_u;
+ xfs_fid_t ha_fid; /* file system specific file ID */
+} xfs_handle_t;
+#define ha_fsid ha_u._ha_fsid
+
+#define XFS_HSIZE(handle) (((char *) &(handle).ha_fid.fid_pad \
+ - (char *) &(handle)) \
+ + (handle).ha_fid.fid_len)
+
+/*
+ * Flags for going down operation
+ */
+#define XFS_FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */
+#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */
+#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */
+
+/*
+ * ioctl commands that are used by Linux filesystems
+ */
+#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS
+#define XFS_IOC_SETXFLAGS FS_IOC_SETFLAGS
+#define XFS_IOC_GETVERSION FS_IOC_GETVERSION
+
+/*
+ * ioctl commands that replace IRIX fcntl()'s
+ * For 'documentation' purposed more than anything else,
+ * the "cmd #" field reflects the IRIX fcntl number.
+ */
+#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64)
+#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64)
+#define XFS_IOC_DIOINFO _IOR ('X', 30, struct dioattr)
+#define XFS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
+#define XFS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr)
+#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64)
+#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64)
+#define XFS_IOC_GETBMAP _IOWR('X', 38, struct getbmap)
+#define XFS_IOC_FSSETDM _IOW ('X', 39, struct fsdmidata)
+#define XFS_IOC_RESVSP _IOW ('X', 40, struct xfs_flock64)
+#define XFS_IOC_UNRESVSP _IOW ('X', 41, struct xfs_flock64)
+#define XFS_IOC_RESVSP64 _IOW ('X', 42, struct xfs_flock64)
+#define XFS_IOC_UNRESVSP64 _IOW ('X', 43, struct xfs_flock64)
+#define XFS_IOC_GETBMAPA _IOWR('X', 44, struct getbmap)
+#define XFS_IOC_FSGETXATTRA _IOR ('X', 45, struct fsxattr)
+/* XFS_IOC_SETBIOSIZE ---- deprecated 46 */
+/* XFS_IOC_GETBIOSIZE ---- deprecated 47 */
+#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap)
+#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64)
+
+/*
+ * ioctl commands that replace IRIX syssgi()'s
+ */
+#define XFS_IOC_FSGEOMETRY_V1 _IOR ('X', 100, struct xfs_fsop_geom_v1)
+#define XFS_IOC_FSBULKSTAT _IOWR('X', 101, struct xfs_fsop_bulkreq)
+#define XFS_IOC_FSBULKSTAT_SINGLE _IOWR('X', 102, struct xfs_fsop_bulkreq)
+#define XFS_IOC_FSINUMBERS _IOWR('X', 103, struct xfs_fsop_bulkreq)
+#define XFS_IOC_PATH_TO_FSHANDLE _IOWR('X', 104, struct xfs_fsop_handlereq)
+#define XFS_IOC_PATH_TO_HANDLE _IOWR('X', 105, struct xfs_fsop_handlereq)
+#define XFS_IOC_FD_TO_HANDLE _IOWR('X', 106, struct xfs_fsop_handlereq)
+#define XFS_IOC_OPEN_BY_HANDLE _IOWR('X', 107, struct xfs_fsop_handlereq)
+#define XFS_IOC_READLINK_BY_HANDLE _IOWR('X', 108, struct xfs_fsop_handlereq)
+#define XFS_IOC_SWAPEXT _IOWR('X', 109, struct xfs_swapext)
+#define XFS_IOC_FSGROWFSDATA _IOW ('X', 110, struct xfs_growfs_data)
+#define XFS_IOC_FSGROWFSLOG _IOW ('X', 111, struct xfs_growfs_log)
+#define XFS_IOC_FSGROWFSRT _IOW ('X', 112, struct xfs_growfs_rt)
+#define XFS_IOC_FSCOUNTS _IOR ('X', 113, struct xfs_fsop_counts)
+#define XFS_IOC_SET_RESBLKS _IOWR('X', 114, struct xfs_fsop_resblks)
+#define XFS_IOC_GET_RESBLKS _IOR ('X', 115, struct xfs_fsop_resblks)
+#define XFS_IOC_ERROR_INJECTION _IOW ('X', 116, struct xfs_error_injection)
+#define XFS_IOC_ERROR_CLEARALL _IOW ('X', 117, struct xfs_error_injection)
+/* XFS_IOC_ATTRCTL_BY_HANDLE -- deprecated 118 */
+/* XFS_IOC_FREEZE -- FIFREEZE 119 */
+/* XFS_IOC_THAW -- FITHAW 120 */
+#define XFS_IOC_FSSETDM_BY_HANDLE _IOW ('X', 121, struct xfs_fsop_setdm_handlereq)
+#define XFS_IOC_ATTRLIST_BY_HANDLE _IOW ('X', 122, struct xfs_fsop_attrlist_handlereq)
+#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
+#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom)
+#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t)
+/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
+
+
+#ifndef HAVE_BBMACROS
+/*
+ * Block I/O parameterization. A basic block (BB) is the lowest size of
+ * filesystem allocation, and must equal 512. Length units given to bio
+ * routines are in BB's.
+ */
+#define BBSHIFT 9
+#define BBSIZE (1<<BBSHIFT)
+#define BBMASK (BBSIZE-1)
+#define BTOBB(bytes) (((uint64_t)(bytes) + BBSIZE - 1) >> BBSHIFT)
+#define BTOBBT(bytes) ((uint64_t)(bytes) >> BBSHIFT)
+#define BBTOB(bbs) ((bbs) << BBSHIFT)
+#endif
+
+#endif /* XFS_FS_H_ */
diff --git a/extlinux/xfs_sb.h b/extlinux/xfs_sb.h
new file mode 100644
index 00000000..8f72d6ad
--- /dev/null
+++ b/extlinux/xfs_sb.h
@@ -0,0 +1,476 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef XFS_SB_H_
+#define XFS_SB_H__
+
+#include <stddef.h>
+
+#include <sys/types.h>
+#include <uuid/uuid.h>
+
+/*
+ * Super block
+ * Fits into a sector-sized buffer at address 0 of each allocation group.
+ * Only the first of these is ever updated except during growfs.
+ */
+
+struct xfs_buf;
+struct xfs_mount;
+
+#define XFS_SB_MAGIC "XFSB" /* 'XFSB' */
+#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */
+#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */
+#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */
+#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */
+#define XFS_SB_VERSION_NUMBITS 0x000f
+#define XFS_SB_VERSION_ALLFBITS 0xfff0
+#define XFS_SB_VERSION_SASHFBITS 0xf000
+#define XFS_SB_VERSION_REALFBITS 0x0ff0
+#define XFS_SB_VERSION_ATTRBIT 0x0010
+#define XFS_SB_VERSION_NLINKBIT 0x0020
+#define XFS_SB_VERSION_QUOTABIT 0x0040
+#define XFS_SB_VERSION_ALIGNBIT 0x0080
+#define XFS_SB_VERSION_DALIGNBIT 0x0100
+#define XFS_SB_VERSION_SHAREDBIT 0x0200
+#define XFS_SB_VERSION_LOGV2BIT 0x0400
+#define XFS_SB_VERSION_SECTORBIT 0x0800
+#define XFS_SB_VERSION_EXTFLGBIT 0x1000
+#define XFS_SB_VERSION_DIRV2BIT 0x2000
+#define XFS_SB_VERSION_BORGBIT 0x4000 /* ASCII only case-insens. */
+#define XFS_SB_VERSION_MOREBITSBIT 0x8000
+#define XFS_SB_VERSION_OKSASHFBITS \
+ (XFS_SB_VERSION_EXTFLGBIT | \
+ XFS_SB_VERSION_DIRV2BIT | \
+ XFS_SB_VERSION_BORGBIT)
+#define XFS_SB_VERSION_OKREALFBITS \
+ (XFS_SB_VERSION_ATTRBIT | \
+ XFS_SB_VERSION_NLINKBIT | \
+ XFS_SB_VERSION_QUOTABIT | \
+ XFS_SB_VERSION_ALIGNBIT | \
+ XFS_SB_VERSION_DALIGNBIT | \
+ XFS_SB_VERSION_SHAREDBIT | \
+ XFS_SB_VERSION_LOGV2BIT | \
+ XFS_SB_VERSION_SECTORBIT | \
+ XFS_SB_VERSION_MOREBITSBIT)
+#define XFS_SB_VERSION_OKREALBITS \
+ (XFS_SB_VERSION_NUMBITS | \
+ XFS_SB_VERSION_OKREALFBITS | \
+ XFS_SB_VERSION_OKSASHFBITS)
+
+/*
+ * There are two words to hold XFS "feature" bits: the original
+ * word, sb_versionnum, and sb_features2. Whenever a bit is set in
+ * sb_features2, the feature bit XFS_SB_VERSION_MOREBITSBIT must be set.
+ *
+ * These defines represent bits in sb_features2.
+ */
+#define XFS_SB_VERSION2_REALFBITS 0x00ffffff /* Mask: features */
+#define XFS_SB_VERSION2_RESERVED1BIT 0x00000001
+#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
+#define XFS_SB_VERSION2_RESERVED4BIT 0x00000004
+#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
+#define XFS_SB_VERSION2_PARENTBIT 0x00000010 /* parent pointers */
+#define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32 bit project id */
+
+#define XFS_SB_VERSION2_OKREALFBITS \
+ (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
+ XFS_SB_VERSION2_ATTR2BIT | \
+ XFS_SB_VERSION2_PROJID32BIT)
+#define XFS_SB_VERSION2_OKSASHFBITS \
+ (0)
+#define XFS_SB_VERSION2_OKREALBITS \
+ (XFS_SB_VERSION2_OKREALFBITS | \
+ XFS_SB_VERSION2_OKSASHFBITS )
+
+/*
+ * Superblock - in core version. Must match the ondisk version below.
+ * Must be padded to 64 bit alignment.
+ */
+typedef struct xfs_sb {
+ uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */
+ uint32_t sb_blocksize; /* logical block size, bytes */
+ xfs_drfsbno_t sb_dblocks; /* number of data blocks */
+ xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */
+ xfs_drtbno_t sb_rextents; /* number of realtime extents */
+ uuid_t sb_uuid; /* file system unique id */
+ xfs_dfsbno_t sb_logstart; /* starting block of log if internal */
+ xfs_ino_t sb_rootino; /* root inode number */
+ xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */
+ xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */
+ xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */
+ xfs_agblock_t sb_agblocks; /* size of an allocation group */
+ xfs_agnumber_t sb_agcount; /* number of allocation groups */
+ xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */
+ xfs_extlen_t sb_logblocks; /* number of log blocks */
+ uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */
+ uint16_t sb_sectsize; /* volume sector size, bytes */
+ uint16_t sb_inodesize; /* inode size, bytes */
+ uint16_t sb_inopblock; /* inodes per block */
+ char sb_fname[12]; /* file system name */
+ uint8_t sb_blocklog; /* log2 of sb_blocksize */
+ uint8_t sb_sectlog; /* log2 of sb_sectsize */
+ uint8_t sb_inodelog; /* log2 of sb_inodesize */
+ uint8_t sb_inopblog; /* log2 of sb_inopblock */
+ uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */
+ uint8_t sb_rextslog; /* log2 of sb_rextents */
+ uint8_t sb_inprogress; /* mkfs is in progress, don't mount */
+ uint8_t sb_imax_pct; /* max % of fs for inode space */
+ /* statistics */
+ /*
+ * These fields must remain contiguous. If you really
+ * want to change their layout, make sure you fix the
+ * code in xfs_trans_apply_sb_deltas().
+ */
+ uint64_t sb_icount; /* allocated inodes */
+ uint64_t sb_ifree; /* free inodes */
+ uint64_t sb_fdblocks; /* free data blocks */
+ uint64_t sb_frextents; /* free realtime extents */
+ /*
+ * End contiguous fields.
+ */
+ xfs_ino_t sb_uquotino; /* user quota inode */
+ xfs_ino_t sb_gquotino; /* group quota inode */
+ uint16_t sb_qflags; /* quota flags */
+ uint8_t sb_flags; /* misc. flags */
+ uint8_t sb_shared_vn; /* shared version number */
+ xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */
+ uint32_t sb_unit; /* stripe or raid unit */
+ uint32_t sb_width; /* stripe or raid width */
+ uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */
+ uint8_t sb_logsectlog; /* log2 of the log sector size */
+ uint16_t sb_logsectsize; /* sector size for the log, bytes */
+ uint32_t sb_logsunit; /* stripe unit size for the log */
+ uint32_t sb_features2; /* additional feature bits */
+
+ /*
+ * bad features2 field as a result of failing to pad the sb
+ * structure to 64 bits. Some machines will be using this field
+ * for features2 bits. Easiest just to mark it bad and not use
+ * it for anything else.
+ */
+ uint32_t sb_bad_features2;
+
+ /* must be padded to 64 bit alignment */
+} xfs_sb_t;
+
+/*
+ * Sequence number values for the fields.
+ */
+typedef enum {
+ XFS_SBS_MAGICNUM, XFS_SBS_BLOCKSIZE, XFS_SBS_DBLOCKS, XFS_SBS_RBLOCKS,
+ XFS_SBS_REXTENTS, XFS_SBS_UUID, XFS_SBS_LOGSTART, XFS_SBS_ROOTINO,
+ XFS_SBS_RBMINO, XFS_SBS_RSUMINO, XFS_SBS_REXTSIZE, XFS_SBS_AGBLOCKS,
+ XFS_SBS_AGCOUNT, XFS_SBS_RBMBLOCKS, XFS_SBS_LOGBLOCKS,
+ XFS_SBS_VERSIONNUM, XFS_SBS_SECTSIZE, XFS_SBS_INODESIZE,
+ XFS_SBS_INOPBLOCK, XFS_SBS_FNAME, XFS_SBS_BLOCKLOG,
+ XFS_SBS_SECTLOG, XFS_SBS_INODELOG, XFS_SBS_INOPBLOG, XFS_SBS_AGBLKLOG,
+ XFS_SBS_REXTSLOG, XFS_SBS_INPROGRESS, XFS_SBS_IMAX_PCT, XFS_SBS_ICOUNT,
+ XFS_SBS_IFREE, XFS_SBS_FDBLOCKS, XFS_SBS_FREXTENTS, XFS_SBS_UQUOTINO,
+ XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN,
+ XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG,
+ XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT,
+ XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2,
+ XFS_SBS_FIELDCOUNT
+} xfs_sb_field_t;
+
+/*
+ * Mask values, defined based on the xfs_sb_field_t values.
+ * Only define the ones we're using.
+ */
+#define XFS_SB_MVAL(x) (1LL << XFS_SBS_ ## x)
+#define XFS_SB_UUID XFS_SB_MVAL(UUID)
+#define XFS_SB_FNAME XFS_SB_MVAL(FNAME)
+#define XFS_SB_ROOTINO XFS_SB_MVAL(ROOTINO)
+#define XFS_SB_RBMINO XFS_SB_MVAL(RBMINO)
+#define XFS_SB_RSUMINO XFS_SB_MVAL(RSUMINO)
+#define XFS_SB_VERSIONNUM XFS_SB_MVAL(VERSIONNUM)
+#define XFS_SB_UQUOTINO XFS_SB_MVAL(UQUOTINO)
+#define XFS_SB_GQUOTINO XFS_SB_MVAL(GQUOTINO)
+#define XFS_SB_QFLAGS XFS_SB_MVAL(QFLAGS)
+#define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN)
+#define XFS_SB_UNIT XFS_SB_MVAL(UNIT)
+#define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH)
+#define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT)
+#define XFS_SB_IFREE XFS_SB_MVAL(IFREE)
+#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS)
+#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2)
+#define XFS_SB_BAD_FEATURES2 XFS_SB_MVAL(BAD_FEATURES2)
+#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT)
+#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1)
+#define XFS_SB_MOD_BITS \
+ (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
+ XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
+ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
+ XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
+ XFS_SB_BAD_FEATURES2)
+
+
+/*
+ * Misc. Flags - warning - these will be cleared by xfs_repair unless
+ * a feature bit is set when the flag is used.
+ */
+#define XFS_SBF_NOFLAGS 0x00 /* no flags set */
+#define XFS_SBF_READONLY 0x01 /* only read-only mounts allowed */
+
+/*
+ * define max. shared version we can interoperate with
+ */
+#define XFS_SB_MAX_SHARED_VN 0
+
+#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS)
+
+static inline int xfs_sb_good_version(xfs_sb_t *sbp)
+{
+ /* We always support version 1-3 */
+ if (sbp->sb_versionnum >= XFS_SB_VERSION_1 &&
+ sbp->sb_versionnum <= XFS_SB_VERSION_3)
+ return 1;
+
+ /* We support version 4 if all feature bits are supported */
+ if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) {
+ if ((sbp->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) ||
+ ((sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) &&
+ (sbp->sb_features2 & ~XFS_SB_VERSION2_OKREALBITS)))
+ return 0;
+
+ if ((sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) &&
+ sbp->sb_shared_vn > XFS_SB_MAX_SHARED_VN)
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Detect a mismatched features2 field. Older kernels read/wrote
+ * this into the wrong slot, so to be safe we keep them in sync.
+ */
+static inline int xfs_sb_has_mismatched_features2(xfs_sb_t *sbp)
+{
+ return (sbp->sb_bad_features2 != sbp->sb_features2);
+}
+
+static inline unsigned xfs_sb_version_tonew(unsigned v)
+{
+ if (v == XFS_SB_VERSION_1)
+ return XFS_SB_VERSION_4;
+
+ if (v == XFS_SB_VERSION_2)
+ return XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT;
+
+ return XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT |
+ XFS_SB_VERSION_NLINKBIT;
+}
+
+static inline unsigned xfs_sb_version_toold(unsigned v)
+{
+ if (v & (XFS_SB_VERSION_QUOTABIT | XFS_SB_VERSION_ALIGNBIT))
+ return 0;
+ if (v & XFS_SB_VERSION_NLINKBIT)
+ return XFS_SB_VERSION_3;
+ if (v & XFS_SB_VERSION_ATTRBIT)
+ return XFS_SB_VERSION_2;
+ return XFS_SB_VERSION_1;
+}
+
+static inline int xfs_sb_version_hasattr(xfs_sb_t *sbp)
+{
+ return sbp->sb_versionnum == XFS_SB_VERSION_2 ||
+ sbp->sb_versionnum == XFS_SB_VERSION_3 ||
+ (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_ATTRBIT));
+}
+
+static inline void xfs_sb_version_addattr(xfs_sb_t *sbp)
+{
+ if (sbp->sb_versionnum == XFS_SB_VERSION_1)
+ sbp->sb_versionnum = XFS_SB_VERSION_2;
+ else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4)
+ sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
+ else
+ sbp->sb_versionnum = XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT;
+}
+
+static inline int xfs_sb_version_hasnlink(xfs_sb_t *sbp)
+{
+ return sbp->sb_versionnum == XFS_SB_VERSION_3 ||
+ (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT));
+}
+
+static inline void xfs_sb_version_addnlink(xfs_sb_t *sbp)
+{
+ if (sbp->sb_versionnum <= XFS_SB_VERSION_2)
+ sbp->sb_versionnum = XFS_SB_VERSION_3;
+ else
+ sbp->sb_versionnum |= XFS_SB_VERSION_NLINKBIT;
+}
+
+static inline int xfs_sb_version_hasquota(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_QUOTABIT);
+}
+
+static inline void xfs_sb_version_addquota(xfs_sb_t *sbp)
+{
+ if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4)
+ sbp->sb_versionnum |= XFS_SB_VERSION_QUOTABIT;
+ else
+ sbp->sb_versionnum = xfs_sb_version_tonew(sbp->sb_versionnum) |
+ XFS_SB_VERSION_QUOTABIT;
+}
+
+static inline int xfs_sb_version_hasalign(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_ALIGNBIT);
+}
+
+static inline int xfs_sb_version_hasdalign(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_DALIGNBIT);
+}
+
+static inline int xfs_sb_version_hasshared(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT);
+}
+
+static inline int xfs_sb_version_hasdirv2(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_DIRV2BIT);
+}
+
+static inline int xfs_sb_version_haslogv2(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_LOGV2BIT);
+}
+
+static inline int xfs_sb_version_hasextflgbit(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT);
+}
+
+static inline int xfs_sb_version_hassector(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
+}
+
+static inline int xfs_sb_version_hasasciici(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_BORGBIT);
+}
+
+static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT);
+}
+
+/*
+ * sb_features2 bit version macros.
+ *
+ * For example, for a bit defined as XFS_SB_VERSION2_FUNBIT, has a macro:
+ *
+ * SB_VERSION_HASFUNBIT(xfs_sb_t *sbp)
+ * ((xfs_sb_version_hasmorebits(sbp) &&
+ * ((sbp)->sb_features2 & XFS_SB_VERSION2_FUNBIT)
+ */
+
+static inline int xfs_sb_version_haslazysbcount(xfs_sb_t *sbp)
+{
+ return xfs_sb_version_hasmorebits(sbp) &&
+ (sbp->sb_features2 & XFS_SB_VERSION2_LAZYSBCOUNTBIT);
+}
+
+static inline int xfs_sb_version_hasattr2(xfs_sb_t *sbp)
+{
+ return xfs_sb_version_hasmorebits(sbp) &&
+ (sbp->sb_features2 & XFS_SB_VERSION2_ATTR2BIT);
+}
+
+static inline void xfs_sb_version_addattr2(xfs_sb_t *sbp)
+{
+ sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
+ sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
+}
+
+static inline void xfs_sb_version_removeattr2(xfs_sb_t *sbp)
+{
+ sbp->sb_features2 &= ~XFS_SB_VERSION2_ATTR2BIT;
+ if (!sbp->sb_features2)
+ sbp->sb_versionnum &= ~XFS_SB_VERSION_MOREBITSBIT;
+}
+
+static inline int xfs_sb_version_hasprojid32bit(xfs_sb_t *sbp)
+{
+ return xfs_sb_version_hasmorebits(sbp) &&
+ (sbp->sb_features2 & XFS_SB_VERSION2_PROJID32BIT);
+}
+
+/*
+ * end of superblock version macros
+ */
+
+#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in filesystem/ag */
+#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR)
+#define XFS_BUF_TO_SBP(bp) ((xfs_dsb_t *)((bp)->b_addr))
+
+#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)XFS_BB_TO_FSBT(mp,d))
+#define XFS_DADDR_TO_FSB(mp,d) XFS_AGB_TO_FSB(mp, \
+ xfs_daddr_to_agno(mp,d), xfs_daddr_to_agbno(mp,d))
+#define XFS_FSB_TO_DADDR(mp,fsbno) XFS_AGB_TO_DADDR(mp, \
+ XFS_FSB_TO_AGNO(mp,fsbno), XFS_FSB_TO_AGBNO(mp,fsbno))
+
+/*
+ * File system sector to basic block conversions.
+ */
+#define XFS_FSS_TO_BB(mp,sec) ((sec) << (mp)->m_sectbb_log)
+
+/*
+ * File system block to basic block conversions.
+ */
+#define XFS_FSB_TO_BB(mp,fsbno) ((fsbno) << (mp)->m_blkbb_log)
+#define XFS_BB_TO_FSB(mp,bb) \
+ (((bb) + (XFS_FSB_TO_BB(mp,1) - 1)) >> (mp)->m_blkbb_log)
+#define XFS_BB_TO_FSBT(mp,bb) ((bb) >> (mp)->m_blkbb_log)
+
+/*
+ * File system block to byte conversions.
+ */
+#define XFS_FSB_TO_B(mp,fsbno) ((xfs_fsize_t)(fsbno) << (mp)->m_sb.sb_blocklog)
+#define XFS_B_TO_FSB(mp,b) \
+ ((((uint64_t)(b)) + (mp)->m_blockmask) >> (mp)->m_sb.sb_blocklog)
+#define XFS_B_TO_FSBT(mp,b) (((uint64_t)(b)) >> (mp)->m_sb.sb_blocklog)
+#define XFS_B_FSB_OFFSET(mp,b) ((b) & (mp)->m_blockmask)
+
+#endif /* XFS_SB_H_ */
diff --git a/extlinux/xfs_types.h b/extlinux/xfs_types.h
new file mode 100644
index 00000000..92808865
--- /dev/null
+++ b/extlinux/xfs_types.h
@@ -0,0 +1,135 @@
+/*
+ * Taken from Linux kernel tree (linux/fs/xfs)
+ *
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef XFS_TYPES_H_
+#define XFS_TYPES_H_
+
+#include <stddef.h>
+
+#include <sys/types.h>
+
+typedef enum { B_FALSE,B_TRUE } boolean_t;
+typedef uint32_t prid_t; /* project ID */
+typedef uint32_t inst_t; /* an instruction */
+
+typedef int64_t xfs_off_t; /* <file offset> type */
+typedef unsigned long long xfs_ino_t; /* <inode> type */
+typedef int64_t xfs_daddr_t; /* <disk address> type */
+typedef char * xfs_caddr_t; /* <core address> type */
+typedef uint32_t xfs_dev_t;
+typedef uint32_t xfs_nlink_t;
+
+/* __psint_t is the same size as a pointer */
+typedef int32_t __psint_t;
+typedef uint32_t __psunsigned_t;
+
+typedef uint32_t xfs_agblock_t; /* blockno in alloc. group */
+typedef uint32_t xfs_extlen_t; /* extent length in blocks */
+typedef uint32_t xfs_agnumber_t; /* allocation group number */
+typedef int32_t xfs_extnum_t; /* # of extents in a file */
+typedef int16_t xfs_aextnum_t; /* # extents in an attribute fork */
+typedef int64_t xfs_fsize_t; /* bytes in a file */
+typedef uint64_t xfs_ufsize_t; /* unsigned bytes in a file */
+
+typedef int32_t xfs_suminfo_t; /* type of bitmap summary info */
+typedef int32_t xfs_rtword_t; /* word type for bitmap manipulations */
+
+typedef int64_t xfs_lsn_t; /* log sequence number */
+typedef int32_t xfs_tid_t; /* transaction identifier */
+
+typedef uint32_t xfs_dablk_t; /* dir/attr block number (in file) */
+typedef uint32_t xfs_dahash_t; /* dir/attr hash value */
+
+/*
+ * These types are 64 bits on disk but are either 32 or 64 bits in memory.
+ * Disk based types:
+ */
+typedef uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */
+typedef uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */
+typedef uint64_t xfs_drtbno_t; /* extent (block) in realtime area */
+typedef uint64_t xfs_dfiloff_t; /* block number in a file */
+typedef uint64_t xfs_dfilblks_t; /* number of blocks in a file */
+
+/*
+ * Memory based types are conditional.
+ */
+typedef uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */
+typedef uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */
+typedef uint64_t xfs_rtblock_t; /* extent (block) in realtime area */
+typedef int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */
+
+typedef uint64_t xfs_fileoff_t; /* block number in a file */
+typedef int64_t xfs_sfiloff_t; /* signed block number in a file */
+typedef uint64_t xfs_filblks_t; /* number of blocks in a file */
+
+/*
+ * Null values for the types.
+ */
+#define NULLDFSBNO ((xfs_dfsbno_t)-1)
+#define NULLDRFSBNO ((xfs_drfsbno_t)-1)
+#define NULLDRTBNO ((xfs_drtbno_t)-1)
+#define NULLDFILOFF ((xfs_dfiloff_t)-1)
+
+#define NULLFSBLOCK ((xfs_fsblock_t)-1)
+#define NULLRFSBLOCK ((xfs_rfsblock_t)-1)
+#define NULLRTBLOCK ((xfs_rtblock_t)-1)
+#define NULLFILEOFF ((xfs_fileoff_t)-1)
+
+#define NULLAGBLOCK ((xfs_agblock_t)-1)
+#define NULLAGNUMBER ((xfs_agnumber_t)-1)
+#define NULLEXTNUM ((xfs_extnum_t)-1)
+
+#define NULLCOMMITLSN ((xfs_lsn_t)-1)
+
+/*
+ * Max values for extlen, extnum, aextnum.
+ */
+#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */
+#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */
+#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */
+
+/*
+ * Min numbers of data/attr fork btree root pointers.
+ */
+#define MINDBTPTRS 3
+#define MINABTPTRS 2
+
+/*
+ * MAXNAMELEN is the length (including the terminating null) of
+ * the longest permissible file (component) name.
+ */
+#define MAXNAMELEN 256
+
+typedef enum {
+ XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi
+} xfs_lookup_t;
+
+typedef enum {
+ XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi,
+ XFS_BTNUM_MAX
+} xfs_btnum_t;
+
+struct xfs_name {
+ const unsigned char *name;
+ int len;
+};
+
+#endif /* XFS_TYPES_H_ */
diff --git a/libinstaller/Makefile b/libinstaller/Makefile
index e67a4686..63446a10 100644
--- a/libinstaller/Makefile
+++ b/libinstaller/Makefile
@@ -1,6 +1,6 @@
# _bin.c files required by both BTARGET and ITARGET installers
BINFILES = bootsect_bin.c ldlinux_bin.c \
- mbr_bin.c gptmbr_bin.c
+ mbr_bin.c gptmbr_bin.c ldlinuxc32_bin.c
PERL = perl
@@ -18,6 +18,9 @@ mbr_bin.c: ../mbr/mbr.bin bin2c.pl
gptmbr_bin.c: ../mbr/gptmbr.bin bin2c.pl
$(PERL) bin2c.pl syslinux_gptmbr < $< > $@
+ldlinuxc32_bin.c: ../com32/elflink/ldlinux/ldlinux.c32 bin2c.pl
+ $(PERL) bin2c.pl syslinux_ldlinuxc32 < $< > $@
+
tidy:
rm -f $(BINFILES)
diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h
index 8b86f881..f60a066e 100644
--- a/libinstaller/syslinux.h
+++ b/libinstaller/syslinux.h
@@ -26,6 +26,9 @@ extern unsigned char syslinux_ldlinux[];
extern const unsigned int syslinux_ldlinux_len;
extern const int syslinux_ldlinux_mtime;
+extern unsigned char syslinux_ldlinuxc32[];
+extern const unsigned int syslinux_ldlinuxc32_len;
+
#define boot_sector syslinux_bootsect
#define boot_sector_len syslinux_bootsect_len
#define boot_image syslinux_ldlinux
diff --git a/libinstaller/syslxfs.h b/libinstaller/syslxfs.h
index 7a231461..4d8f3b2c 100644
--- a/libinstaller/syslxfs.h
+++ b/libinstaller/syslxfs.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com>
+ * Copyright 2011-2012 Paulo Alcantara <pcacjr@zytor.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -12,13 +12,14 @@
#ifndef _SYSLXFS_H_
#define _SYSLXFS_H_
-/* Global fs_type for handling fat, ntfs, ext2/3/4 and btrfs */
+/* Global fs_type for handling fat, ntfs, ext2/3/4, btrfs and xfs */
enum filesystem {
NONE,
EXT2,
BTRFS,
VFAT,
NTFS,
+ XFS,
};
extern int fs_type;
diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h
index e5428b79..59e3e5ef 100644
--- a/libinstaller/syslxint.h
+++ b/libinstaller/syslxint.h
@@ -124,23 +124,6 @@ static inline void set_64(uint64_t *p, uint64_t v)
*/
#ifdef __MSDOS__
-static inline __attribute__ ((const))
-uint16_t ds(void)
-{
- uint16_t v;
- asm("movw %%ds,%0":"=rm"(v));
- return v;
-}
-
-static inline void *set_fs(const void *p)
-{
- uint16_t seg;
-
- seg = ds() + ((size_t) p >> 4);
- asm volatile ("movw %0,%%fs"::"rm" (seg));
- return (void *)((size_t) p & 0xf);
-}
-
uint8_t get_8_sl(const uint8_t * p);
uint16_t get_16_sl(const uint16_t * p);
uint32_t get_32_sl(const uint32_t * p);
diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c
index c706f2c2..98e2710a 100644
--- a/libinstaller/syslxmod.c
+++ b/libinstaller/syslxmod.c
@@ -147,11 +147,13 @@ int syslinux_patch(const sector_t *sectp, int nsectors,
ex = ptr(boot_image, &epa->secptroffset);
nptrs = get_16_sl(&epa->secptrcnt);
+#if 0
if (nsect > nptrs) {
/* Not necessarily an error in this case, but a general problem */
fprintf(stderr, "Insufficient extent space, build error!\n");
exit(1);
}
+#endif
/* -1 for the pointer in the boot sector, -2 for the two ADVs */
generate_extents(ex, nptrs, sectp, nsect-1-2);
diff --git a/linux/Makefile b/linux/Makefile
index 08a3ed49..d7facaf4 100644
--- a/linux/Makefile
+++ b/linux/Makefile
@@ -31,6 +31,7 @@ SRCS = syslinux.c \
../libinstaller/fs.c \
../libinstaller/syslxmod.c \
../libinstaller/bootsect_bin.c \
+ ../libinstaller/ldlinuxc32_bin.c \
../libinstaller/ldlinux_bin.c
OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS)))
diff --git a/linux/syslinux.c b/linux/syslinux.c
index 4b13b7fe..f64834bd 100755
--- a/linux/syslinux.c
+++ b/linux/syslinux.c
@@ -238,6 +238,24 @@ int modify_existing_adv(const char *path)
return 0;
}
+int do_open_file(char *name)
+{
+ int fd;
+
+ if ((fd = open(name, O_RDONLY)) >= 0) {
+ uint32_t zero_attr = 0;
+ ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &zero_attr);
+ close(fd);
+ }
+
+ unlink(name);
+ fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0444);
+ if (fd < 0)
+ perror(name);
+
+ return fd;
+}
+
int main(int argc, char *argv[])
{
static unsigned char sectbuf[SECTOR_SIZE];
@@ -253,7 +271,7 @@ int main(int argc, char *argv[])
const char *errmsg;
int mnt_cookie;
int patch_sectors;
- int i;
+ int i, rv;
mypid = getpid();
umask(077);
@@ -408,16 +426,8 @@ int main(int argc, char *argv[])
if (modify_adv() < 0)
exit(1);
- if ((fd = open(ldlinux_name, O_RDONLY)) >= 0) {
- uint32_t zero_attr = 0;
- ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &zero_attr);
- close(fd);
- }
-
- unlink(ldlinux_name);
- fd = open(ldlinux_name, O_WRONLY | O_CREAT | O_TRUNC, 0444);
+ fd = do_open_file(ldlinux_name);
if (fd < 0) {
- perror(opt.device);
err = 1;
goto umount;
}
@@ -451,6 +461,31 @@ int main(int argc, char *argv[])
close(fd);
sync();
+ sprintf(ldlinux_name, "%sldlinux.c32", ldlinux_path);
+ fd = do_open_file(ldlinux_name);
+ if (fd < 0) {
+ err = 1;
+ goto umount;
+ }
+
+ rv = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0);
+ if (rv != (int)syslinux_ldlinuxc32_len) {
+ fprintf(stderr, "%s: write failure on %s\n", program, ldlinux_name);
+ exit(1);
+ }
+
+ fsync(fd);
+ /*
+ * Set the attributes
+ */
+ {
+ uint32_t attr = 0x07; /* Hidden+System+Readonly */
+ ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
+ }
+
+ close(fd);
+ sync();
+
umount:
do_umount(mntpath, mnt_cookie);
sync();
diff --git a/lzo/prepcore.c b/lzo/prepcore.c
index 1d400670..9147b2e4 100644
--- a/lzo/prepcore.c
+++ b/lzo/prepcore.c
@@ -340,10 +340,12 @@ int main(int argc, char *argv[])
set_32((uint32_t *) (infile + soff + 4), csum);
}
+ /*
if (offset+outfile_len > get_32(&prefix->pfx_maxlma))
error("output too big (%lu, max %lu)",
(unsigned long)offset+outfile_len,
(unsigned long)get_32(&prefix->pfx_maxlma));
+ */
f = fopen(out_name, "wb");
if (!f)
diff --git a/man/syslinux.1 b/man/syslinux.1
index af44979b..adcaf94d 100644
--- a/man/syslinux.1
+++ b/man/syslinux.1
@@ -141,8 +141,7 @@ and if no "append" is given the default is to use the global entry (if any).
Use "append -" to use no options at all. Up to 128 "label" entries are
permitted.
.IP
-The "image" doesn't have to be a Linux kernel; it can be a boot sector or a
-COMBOOT file (see below.)
+The "image" doesn't have to be a Linux kernel; it can be a boot sector (see below.)
.RE
.TP
.BI implicit\ flag_val
@@ -297,25 +296,21 @@ For example:
.TP
\fI<SUB>\fP = \fI<Ctrl-Z>\fP = ASCII 26
End of file (DOS convention).
-.SS Comboot Images and other operating systems
+.SS Other operating systems
This version of \fBsyslinux\fP supports chain loading of other operating
-systems (such as MS-DOS and its derivatives, including Windows 95/98),
-as well as COMBOOT-style standalone executables (a subset of DOS .COM
-files; see separate section below.)
+systems (such as MS-DOS and its derivatives, including Windows 95/98).
.PP
Chain loading requires the boot sector of the foreign operating system
to be stored in a file in the root directory of the filesystem.
-Because neither Linux kernels, boot sector images, nor COMBOOT files
-have reliable magic numbers, \fBsyslinux\fP will look at the file
+Because neither Linux kernels, nor boot sector images have reliable magic
+numbers, \fBsyslinux\fP will look at the file
extension. The following extensions are recognised:
.PP
.nf
.ta \w'none or other 'u
none or other Linux kernel image
-CBT COMBOOT image (not runnable from DOS)
BSS Boot sector (DOS superblock will be patched in)
BS Boot sector
-COM COMBOOT image (runnable from DOS)
.fi
.PP
For filenames given on the command line, \fBsyslinux\fP will search for the
@@ -323,29 +318,6 @@ file by adding extensions in the order listed above if the plain
filename is not found. Filenames in KERNEL statements must be fully
qualified.
.PP
-A COMBOOT file is a standalone executable in DOS .COM format. They
-can, among other things, be produced by the Etherboot package by
-Markus Gutschke and Ken Yap. The following requirements apply for
-these files to be sufficiently "standalone" for \fBsyslinux\fP to be able to
-load and run them:
-.IP \(bu
-The program must not execute any DOS calls (since there is no
-DOS), although it may call the BIOS. The only exception is that
-the program may execute INT 20h (Terminate Program) to return to
-the \fBsyslinux\fP prompt. Note especially that INT 21h AH=4Ch, INT 21h
-AH=31h or INT 27h are not supported.
-.IP \(bu
-Only the fields pspInt20 at offset 00h, pspNextParagraph at offset 02h and
-pspCommandTail at offset 80h (contains the arguments from the \fBsyslinux\fP command
-line) in the PSP are supported. All other fields will contain zero.
-.IP \(bu
-The program must not modify any main memory outside its 64K segment if it
-returns to \fBsyslinux\fP via INT 20h.
-.PP
-\fBSyslinux\fP currently doesn't provide any form of API for the use of
-COMBOOT files. If there is need, a future version may contain an INT
-interface to some \fBsyslinux\fP functions; please contact me if you have a
-need or ideas for such an API.
.SS Novice protection
\fBSyslinux\fP will attempt to detect if the user is trying to boot on a 286
or lower class machine, or a machine with less than 608K of low ("DOS")
diff --git a/mbr/gptmbr.S b/mbr/gptmbr.S
index a6a52ed2..c1db4e87 100644
--- a/mbr/gptmbr.S
+++ b/mbr/gptmbr.S
@@ -77,6 +77,12 @@ next:
ADJUST_DRIVE
pushw %dx /* 0(%bp) = %dl -> drive number */
+ movw %sp, %bp /* %bp -> frame pointer: LEAVE UNCHANGED */
+
+ /* prepare to read sector size */
+ sub $0x1c, %sp /* -28(%bp) == %sp */
+ pushw $0x1e /* -30(%bp) == %sp */
+ movw $0x200, -6(%bp) /* -6(%bp) sector size */
/* Check to see if we have EBIOS */
pushw %dx /* drive number */
@@ -86,6 +92,8 @@ next:
xorb %dh, %dh
stc
int $0x13
+ popw %dx /* restore drive */
+ movb $0x08, %ah /* get CHS geometry */
jc 1f
cmpw $0xaa55, %bx
jne 1f
@@ -97,41 +105,43 @@ next:
movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
(read_sector_cbios)
-1:
- popw %dx
+ /*
+ * read sector size.
+ * Should not fail but if it does I assume that at least
+ * previous 512 value is not overridden
+ */
+ movb $0x48, %ah
+ movw %sp, %si
+1:
/* Get (C)HS geometry */
- movb $0x08, %ah
int $0x13
+
+ /* here we computer CHS values or just do some dummy computation for EBIOS */
andw $0x3f, %cx /* Sector count */
- movw %sp, %bp /* %bp -> frame pointer: LEAVE UNCHANGED */
- pushw %cx /* -2(%bp) Save sectors on the stack */
+ pushw %cx /* -32(%bp) Save sectors on the stack */
movzbw %dh, %ax /* dh = max head */
incw %ax /* From 0-based max to count */
mulw %cx /* Heads*sectors -> sectors per cylinder */
/* Save sectors/cylinder on the stack */
- pushw %dx /* -4(%bp) High word */
- pushw %ax /* -6(%bp) Low word */
+ pushw %dx /* -34(%bp) High word */
+ pushw %ax /* -36(%bp) Low word */
/* Load partition table header */
xorl %eax,%eax
cltd
incw %ax /* %edx:%eax = 1 */
- movw $phdr, %bx
- pushw %bx /* -8(%bp) phdr == bootsect */
- call read_sector
+ call read_sector_phdr
/* Number of partition sectors */
- /* We assume the partition table is 32K or less, and that
- the sector size is 512. */
- /* Note: phdr == 6(%bp) */
+ /* We assume the partition table is 32K or less */
movw (80+6)(%bp),%cx /* NumberOfPartitionEntries */
movw (84+6)(%bp),%ax /* SizeOfPartitionEntry */
pushw %ax
pushw %cx
mulw %cx
- shrw $9,%ax
+ divw -6(%bp) /* %dx == 0 here */
xchgw %ax,%cx
incw %cx
@@ -142,7 +152,6 @@ next:
pushw %bx
get_ptab:
call read_sector
- call inc64
loopw get_ptab
/* Find the boot partition */
@@ -216,9 +225,8 @@ found_part:
boot:
movl (32+20)(%si),%eax
movl (36+20)(%si),%edx
- popw %bx
- call read_sector
- cmpw $0xaa55, -2(%bx)
+ call read_sector_phdr
+ cmpw $0xaa55, (0x7c00+0x1fe)
jne missing_os /* Not a valid boot sector */
movw %bp, %sp /* driveno == bootsec-6 */
popw %dx /* dl -> drive number */
@@ -239,17 +247,15 @@ saturate_stosl:
1: stosl
ret
-/*
- * Increment %edx:%eax
- */
-inc64:
- addl $1,%eax
- adcl $0,%edx
- ret
+read_sector_phdr:
+ movw $phdr, %bx
+
+ /* fall through and read sector */
/*
* read_sector: read a single sector pointed to by %edx:%eax to
* %es:%bx. CF is set on error. All registers saved.
+ * %edx:%eax and %es:%bx are incremented to read next sector
*/
read_sector:
pushal
@@ -264,12 +270,12 @@ read_sector:
/* This chunk is skipped if we have ebios */
/* Do not clobber %es:%bx or %edx:%eax before this chunk! */
read_sector_cbios:
- divl -6(%bp) /* secpercyl */
+ divl -36(%bp) /* secpercyl */
shlb $6, %ah
movb %ah, %cl
movb %al, %ch
xchgw %dx, %ax
- divb -2(%bp) /* sectors */
+ divb -32(%bp) /* sectors */
movb %al, %dh
orb %ah, %cl
incw %cx /* Sectors are 1-based */
@@ -281,12 +287,21 @@ read_common:
leaw 16(%si), %sp /* Drop DAPA */
popal
jc disk_error
- addb $2, %bh /* bx += 512: point to the next buffer */
+ addb -5(%bp), %bh /* bx += sector size: point to the next buffer */
+
+ /* fall through and increment sector number */
+
+/*
+ * Increment %edx:%eax
+ */
+inc64:
+ addl $1,%eax
+ adcl $0,%edx
ret
disk_error:
call error
- .ascii "Disk error on boot\r\n"
+ .ascii "Disk error\r\n"
/*
* Print error messages. This is invoked with "call", with the
diff --git a/mbr/mbr.S b/mbr/mbr.S
index b71cfb7c..270a3568 100644
--- a/mbr/mbr.S
+++ b/mbr/mbr.S
@@ -265,6 +265,19 @@ boot:
movl %eax, 8(%si) /* Adjust in-memory partition table entry */
call read_sector
jc disk_error
+
+ /* Check if the read sector is a XFS superblock */
+ cmpl $0x42534658, (bootsec) /* "XFSB" */
+ jne no_xfs
+
+ /* We put the Syslinux boot sector at offset 0x800 (4 sectors), so we
+ * need to adjust %eax (%eax + 4) to read the right sector into 0x7C00.
+ */
+ addl $0x800 >> 0x09, %eax /* plus 4 sectors */
+ call read_sector
+ jc disk_error
+
+no_xfs:
cmpw $0xaa55, (bootsec+510)
jne missing_os /* Not a valid boot sector */
movw $driveno, %sp /* driveno == bootsec-6 */
diff --git a/mk/com32.mk b/mk/com32.mk
index 4a6caae9..bfba0e1b 100644
--- a/mk/com32.mk
+++ b/mk/com32.mk
@@ -54,8 +54,8 @@ SFLAGS = $(GCCOPT) $(GCCWARN) -march=i386 \
-nostdinc -iwithprefix include \
-I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE)
-COM32LD = $(com32)/lib/com32.ld
-LDFLAGS = -m elf_i386 --emit-relocs -T $(COM32LD)
+COM32LD = $(com32)/lib/elf32.ld
+LDFLAGS = -m elf_i386 -shared --hash-style=gnu -T $(COM32LD)
LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc)
LNXCFLAGS = -I$(com32)/libutil/include $(GCCWARN) -O -g \
@@ -63,8 +63,7 @@ LNXCFLAGS = -I$(com32)/libutil/include $(GCCWARN) -O -g \
LNXSFLAGS = -g
LNXLDFLAGS = -g
-C_LIBS = $(com32)/libutil/libutil_com.a $(GPLLIB) \
- $(com32)/lib/libcom32.a $(LIBGCC)
+C_LIBS = $(GPLLIB) $(com32)/lib/libcom32.c32 $(LIBGCC)
C_LNXLIBS = $(com32)/libutil/libutil_lnx.a
.SUFFIXES: .lss .c .lo .o .elf .c32 .lnx
@@ -94,5 +93,6 @@ C_LNXLIBS = $(com32)/libutil/libutil_lnx.a
$(CC) $(LNXCFLAGS) -o $@ $^
%.c32: %.elf
- $(OBJCOPY) -O binary $< $@
- $(RELOCS) $< >> $@ || ( rm -f $@ ; false )
+ $(OBJCOPY) --strip-debug --strip-unneeded $< $@
+ ##$(OBJCOPY) -O binary $< $@
+ ##$(RELOCS) $< >> $@ || ( rm -f $@ ; false )
diff --git a/mk/devel.mk b/mk/devel.mk
index 104207f5..421c2e45 100644
--- a/mk/devel.mk
+++ b/mk/devel.mk
@@ -1,2 +1,9 @@
# Useful while doing development, but not for production.
-GCCWARN += -Wno-clobbered -Werror
+GCCWARN += -Wno-clobbered
+#GCCWARN += -DDEBUG_MALLOC
+# GCCWARN += -DDEBUG_PORT=0x3f8 -DDEBUG=1
+GCCWARN += -DDYNAMIC_DEBUG
+
+## The following will enable printing ethernet/arp/ip/icmp/tcp/udp headers
+## in undiif.c
+# GCCWARN += -DUNDIIF_ID_DEBUG=0x80U -DLWIP_DEBUG -DDEBUG_PORT=0x3f8
diff --git a/mk/elf.mk b/mk/elf.mk
new file mode 100644
index 00000000..8bc6be08
--- /dev/null
+++ b/mk/elf.mk
@@ -0,0 +1,89 @@
+## -*- makefile -*- -------------------------------------------------------
+##
+## Copyright 2008 H. Peter Anvin - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+## Boston MA 02110-1301, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## ELF common configurables
+##
+
+include $(MAKEDIR)/syslinux.mk
+
+GCCOPT := $(call gcc_ok,-std=gnu99,)
+GCCOPT += $(call gcc_ok,-m32,)
+GCCOPT += $(call gcc_ok,-fno-stack-protector,)
+GCCOPT += $(call gcc_ok,-fwrapv,)
+GCCOPT += $(call gcc_ok,-freg-struct-return,)
+GCCOPT += -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3
+GCCOPT += $(call gcc_ok,-fno-exceptions,)
+GCCOPT += $(call gcc_ok,-fno-asynchronous-unwind-tables,)
+GCCOPT += $(call gcc_ok,-fPIE,-fPIC)
+GCCOPT += $(call gcc_ok,-falign-functions=0,-malign-functions=0)
+GCCOPT += $(call gcc_ok,-falign-jumps=0,-malign-jumps=0)
+GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
+GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
+GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
+
+com32 = $(topdir)/com32
+core = $(topdir)/core
+
+ifneq ($(NOGPL),1)
+GPLLIB = $(com32)/gpllib/libgpl.c32
+GPLINCLUDE = -I$(com32)/gplinclude
+else
+GPLLIB =
+GPLINCLUDE =
+endif
+
+CFLAGS = $(GCCOPT) $(GCCWARN) -W -Wall -march=i386 \
+ -fomit-frame-pointer -D__COM32__ -DDYNAMIC_MODULE \
+ -nostdinc -iwithprefix include \
+ -I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE) \
+ -I$(core)/include
+SFLAGS = $(GCCOPT) -D__COM32__ -march=i386
+LDFLAGS = -m elf_i386 -shared --hash-style=gnu -T $(com32)/lib/elf32.ld --as-needed
+
+LNXCFLAGS = -I$(com32)/libutil/include -W -Wall -O -g -D_GNU_SOURCE
+LNXSFLAGS = -g
+LNXLDFLAGS = -g
+
+C_LIBS = $(com32)/libutil/libutil.c32 $(GPLLIB) \
+ $(com32)/lib/libcom32.c32
+C_LNXLIBS = $(com32)/libutil/libutil_lnx.a \
+ $(com32)/elflink/ldlinux/ldlinux_lnx.a
+
+.SUFFIXES: .lss .c .o
+
+.PRECIOUS: %.o
+%.o: %.S
+ $(CC) $(SFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.o
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.lo
+%.lo: %.S
+ $(CC) $(LNXSFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.lo
+%.lo: %.c
+ $(CC) $(LNXCFLAGS) -c -o $@ $<
+
+.PRECIOUS: %.lnx
+%.lnx: %.lo $(LNXLIBS) $(C_LNXLIBS)
+ $(CC) $(LNXCFLAGS) -o $@ $^
+
+.PRECIOUS: %.elf
+%.elf: %.o $(C_LIBS)
+ $(LD) $(LDFLAGS) -o $@ $^
+
+%.c32: %.elf
+ $(OBJCOPY) --strip-debug --strip-unneeded $< $@
diff --git a/mk/embedded.mk b/mk/embedded.mk
index e8f3ae30..c2f4edf3 100644
--- a/mk/embedded.mk
+++ b/mk/embedded.mk
@@ -32,6 +32,7 @@ GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
GCCOPT += $(call gcc_ok,-mincoming-stack-boundary=2,)
+GCCOPT += $(call gcc_ok,-fvisibility=hidden)
LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc)
diff --git a/mk/lib.mk b/mk/lib.mk
index 48b2a13d..ca8e41ba 100644
--- a/mk/lib.mk
+++ b/mk/lib.mk
@@ -32,13 +32,14 @@ LIBFLAGS = -DDYNAMIC_CRC_TABLE -DPNG_NO_CONSOLE_IO \
# LIBFLAGS += -DPNG_NO_FLOATING_POINT_SUPPORTED
REQFLAGS = $(GCCOPT) -g -mregparm=3 -DREGPARM=3 -D__COM32__ \
- -nostdinc -iwithprefix include -I. -I./sys -I../include
+ -nostdinc -iwithprefix include -I. -I./sys -I../include \
+ -I../../core/include
OPTFLAGS = -Os -march=i386 -falign-functions=0 -falign-jumps=0 \
-falign-labels=0 -ffast-math -fomit-frame-pointer
WARNFLAGS = $(GCCWARN) -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -Winline
CFLAGS = $(OPTFLAGS) $(REQFLAGS) $(WARNFLAGS) $(LIBFLAGS)
-LDFLAGS = -m elf32_i386
+LDFLAGS = -m elf_i386 --hash-style=gnu -T $(com32)/lib/elf32.ld
.SUFFIXES: .c .o .a .so .lo .i .S .s .ls .ss .lss
@@ -78,3 +79,6 @@ LDFLAGS = -m elf32_i386
.c.ls:
$(CC) $(MAKEDEPS) $(CFLAGS) $(SOFLAGS) -S -o $@ $<
+
+%.c32: %.elf
+ $(OBJCOPY) --strip-debug --strip-unneeded $< $@
diff --git a/mk/rosh.mk b/mk/rosh.mk
index 7fdba0f1..45b98213 100644
--- a/mk/rosh.mk
+++ b/mk/rosh.mk
@@ -15,7 +15,7 @@
##
## Include the COM32 common configurables
-include $(MAKEDIR)/com32.mk
+include $(MAKEDIR)/elf.mk
# CFLAGS = $(GCCOPT) $(GCCWARN) -march=i386 \
# -fomit-frame-pointer -D__COM32__ \
diff --git a/mk/syslinux.mk b/mk/syslinux.mk
index 6d87187f..2e12072e 100644
--- a/mk/syslinux.mk
+++ b/mk/syslinux.mk
@@ -32,10 +32,16 @@ COM32DIR = $(AUXDIR)/com32
BOOTDIR = /boot
EXTLINUXDIR = $(BOOTDIR)/extlinux
+ifdef DEBUG
+# This allows DEBUGOPT to be set from the command line
+DEBUGOPT = -DDEBUG=$(DEBUG)
+endif
+
NASM = nasm
-NASMOPT = -Ox
+NASMOPT = -Ox $(DEBUGOPT)
PERL = perl
+PYTHON = python
UPX = upx
CHMOD = chmod
@@ -50,6 +56,7 @@ gcc_ok = $(shell tmpf=gcc_ok.$$$$.tmp; \
LD = ld
OBJDUMP = objdump
OBJCOPY = objcopy
+STRIP = strip
AR = ar
NM = nm
RANLIB = ranlib
@@ -66,9 +73,7 @@ WGET = wget
com32 = $(topdir)/com32
# Common warnings we want for all gcc-generated code
-GCCWARN := -W -Wall -Wstrict-prototypes
-# Extremely useful variant for debugging...
-#GCCWARN += -Wno-clobbered -Werror
+GCCWARN = -W -Wall -Wstrict-prototypes $(DEBUGOPT)
# Common stanza to make gcc generate .*.d dependency files
MAKEDEPS = -Wp,-MT,$@,-MD,$(dir $@).$(notdir $@).d
diff --git a/modules/Makefile b/modules/Makefile
deleted file mode 100644
index f878c70d..00000000
--- a/modules/Makefile
+++ /dev/null
@@ -1,61 +0,0 @@
-## -----------------------------------------------------------------------
-##
-## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-## Boston MA 02111-1307, USA; either version 2 of the License, or
-## (at your option) any later version; incorporated herein by reference.
-##
-## -----------------------------------------------------------------------
-
-##
-## Non-COM32 simple Syslinux modules
-##
-
-topdir = ..
-MAKEDIR = $(topdir)/mk
-include $(MAKEDIR)/embedded.mk
-
-INCLUDES = -I$(com32)/include
-
-BINS = pxechain.com poweroff.com int18.com ver.com
-
-all: $(BINS)
-
-.PRECIOUS: %.o
-
-.PRECIOUS: %.elf
-%.elf: c32entry.o %.o $(LIB)
- $(LD) -Ttext 0x101000 -e _start -o $@ $^
-
-%.c32: %.elf
- $(OBJCOPY) -O binary $< $@
-
-%.com: %.asm
- ( $(NASM) -M -DDEPEND -o $@ $< ; echo '' ) > .$@.d ; true
- $(NASM) $(NASMOPT) -f bin -o $@ -l $*.lst $<
-
-$(LIB): $(LIBOBJS)
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
-
-%.lss: %.ppm.gz $(PPMTOLSS16)
- $(GZIPPROG) -cd $< | \
- $(PPMTOLSS16) \#000000=0 \#d0d0d0=7 \#f6f6f6=15 \
- > $@
-
-%.ppm.gz: %.png
- $(PNGTOPNM) $< | gzip -9 > $@
-
-tidy dist:
- rm -f *.o *.a *.lst *.elf *.map .*.d
-
-clean: tidy
-
-spotless: clean
- rm -f $(BINS)
-
--include .*.d
diff --git a/modules/int18.asm b/modules/int18.asm
deleted file mode 100644
index a13ada75..00000000
--- a/modules/int18.asm
+++ /dev/null
@@ -1,16 +0,0 @@
- bits 16
- org 100h
-_start:
- mov ax,5
- int 22h
- mov ah,09h
- mov dx,msg
- int 21h
- mov ax,000Ch
- xor dx,dx
- int 22h
- int 18h
- jmp 0F000h:0FFF0h ; INT 18h should not return...
-
- section .data
-msg: db 'Local boot via INT 18...', 13, 10, '$'
diff --git a/modules/poweroff.asm b/modules/poweroff.asm
deleted file mode 100644
index f4f19a2f..00000000
--- a/modules/poweroff.asm
+++ /dev/null
@@ -1,102 +0,0 @@
-; ****************************************************************************
-;
-; poweroff.asm
-;
-; Copyright 2009 Sebastian Herbszt
-;
-; APM poweroff module.
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-; Boston MA 02111-1307, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; ****************************************************************************
-
- absolute 0
-pspInt20: resw 1
-pspNextP: resw 1
- resb 124
-pspCmdLen: resb 1
-pspCmdArg: resb 127
-
- section .text
- org 0x100
-
-_start:
- mov ax,5300h ; APM Installation Check (00h)
- xor bx,bx ; APM BIOS (0000h)
- int 15h
- jnc check_sig
-
- mov bx, msg_notpresent
- jmp error
-
-check_sig:
- cmp bx,504Dh ; signature 'PM'
- je check_ver
-
- mov bx, msg_notpresent
- jmp error
-
-check_ver:
- cmp ax,0101h ; Need version 1.1+
- jae check_state
-
- mov bx, msg_notsup
- jmp error
-
-check_state:
- test cx,8 ; bit 3 APM BIOS Power Management disabled
- jz connect
-
- mov bx, msg_pmdisabled
- jmp error
-
-connect:
- mov ax,5301h ; APM Real Mode Interface Connect (01h)
- xor bx,bx ; APM BIOS (0000h)
- int 15h
- jnc connect_ok
-
- mov bx, msg_connect
- jmp error
-
-connect_ok:
- mov ax,530Eh ; APM Driver Version (0Eh)
- xor bx,bx ; APM BIOS (0000h)
- mov cx,0101h ; APM Driver version 1.1
- int 15h
- jnc apm0101_check
-
- mov bx, msg_notsup
- jmp error
-
-apm0101_check:
- cmp cx,0101h ; APM Connection version
- jae apm0101_ok
-
- mov bx, msg_notsup
- jmp error
-
-apm0101_ok:
- mov ax,5307h ; Set Power State (07h)
- mov bx,0001h ; All devices power managed by the APM BIOS
- mov cx,0003h ; Power state off
- int 15h
- jnc off
-
- mov bx, msg_failed
-
-error:
- mov ax,2
- int 22h
-off:
- ret
-
-msg_notpresent: db 'APM not present.',0dh,0ah,0
-msg_notsup: db 'APM 1.1+ not supported.',0dh,0ah,0
-msg_pmdisabled: db 'Power management disabled.',0dh,0ah,0
-msg_connect: db 'APM RM interface connect failed.',0dh,0ah,0
-msg_failed: db 'Power off failed.',0dh,0ah,0
diff --git a/modules/pxechain.asm b/modules/pxechain.asm
deleted file mode 100644
index 35c19691..00000000
--- a/modules/pxechain.asm
+++ /dev/null
@@ -1,558 +0,0 @@
-; "$Id: pxechain.asm,v 1.2 2007/12/16 08:15:39 jhutz Exp $"
-; -*- fundamental -*- (asm-mode sucks) vim:noet:com=\:;
-; ****************************************************************************
-;
-; pxechain.asm
-;
-; A comboot program to chain from PXELINUX to another PXE network
-; bootstrap program (NBP). This improves on PXELINUX's built-in PXE
-; chaining support by arranging for the server address and boot filename
-; reported by the PXE stack to be those from which the new NBP was
-; loaded, allowing PXELINUX to be used to select from multiple NBP's,
-; such as gPXE, another PXELINUX(*), Windows RIS, and so on.
-;
-; (*) This seems unnecessary at first, but it is very helpful when
-; selecting from among self-contained network boot images.
-;
-; Copyright (c) 2007 Carnegie Mellon University
-; Copyright (C) 1994-2007 H. Peter Anvin
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-; Boston MA 02111-1307, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; ****************************************************************************
-
-;%define DEBUG
-;%define NO_RUN
-
- absolute 0
-pspInt20: resw 1
-pspNextP: resw 1
- resb 124
-pspCmdLen: resb 1
-pspCmdArg: resb 127
-
- section .text
- org 0x100
-
-%ifdef DEBUG
-%macro MARK 1.nolist
- mov ah,0x02
- mov dl,%1&0xff
- int 0x21
-%if (%1 >> 8) & 0xff
- mov dl,(%1 >> 8) & 0xff
- int 0x21
-%if (%1 >> 16) & 0xff
- mov dl,(%1 >> 16) & 0xff
- int 0x21
-%if (%1 >> 24) & 0xff
- mov dl,(%1 >> 24) & 0xff
- int 0x21
-%endif
-%endif
-%endif
- mov dl,' '
- int 0x21
-%endmacro
-%macro SHOWD 1.nolist
- mov al,%1
- call print_dec
- mov ah,0x02
- mov dl,' '
- int 0x21
-%endmacro
-%macro SHOWX 1.nolist
- mov bx,%1
- call print_hex
- mov ah,0x02
- mov dl,' '
- int 0x21
-%endmacro
-%else
-%macro MARK 1.nolist
-%endmacro
-%macro SHOWD 1.nolist
-%endmacro
-%macro SHOWX 1.nolist
-%endmacro
-%endif
-
-_start:
- MARK 'INIT'
-
-; There should be exactly one command-line argument, which is of the form
-; [[ipaddress]::]tftp_filename, just like filenames given to PXELINUX.
-; Too few or too many arguments is an error.
-;
-; This code is based on mangle_name in pxelinux.asm
-parse_args:
- cld
- xor cx,cx
- mov cl,[pspCmdLen]
- dec cx
- mov si,pspCmdArg+1
- and cx,cx
- je near usage ; no args is bad
- add si,cx
- dec si
- std
-.chomp: lodsb
- cmp al,' '
- loopz .chomp
- inc cx
- cld
- mov [pspCmdLen],cl
- mov si,pspCmdArg+1
- cmp word [si],'::' ; Leading ::?
- je near gotprefix
- dec cx
- jz noip
- MARK 'SCAN'
-
-.more:
- inc si
- cmp byte [si],' '
- je near usage
- cmp word [si],'::'
- je near parse_ip
- loop .more
-
-noip:
- MARK 'NOIP'
- mov ax,0x0e ; get config file name
- int 0x22
- mov si,bx
-%ifdef DEBUG
- mov ah,0x02
- mov dl,'['
- int 0x21
- mov ax,0x02
- int 0x22
- mov ah,0x02
- mov dl,']'
- int 0x21
- mov dl,' '
- int 0x21
-%endif
- push ds
- push es
- pop ds
- pop es
- push es
-.find_prefix:
- lodsb
- and al,al
- jnz .find_prefix
- dec si
-
- mov cx,si
- sub cx,bx
- MARK 'LEN'
- SHOWD cl ; assume it's <256 for debugging
- dec si
- std
-.find_slash:
- lodsb
- cmp al,'/'
- je .slash
- loop .find_slash
-.slash:
- cmp cx,127
- cld
- jna .copy_prefix
- pop ds
- jmp too_long
-
-.copy_prefix:
- SHOWD cl
- MARK 'PFX'
- mov si,bx
- mov di,tftp_filename
- mov bx,128
- sub bx,cx
- rep movsb
- pop ds
-
- mov cl,[pspCmdLen]
- mov si,pspCmdArg+1
- jmp prefix_done
-
-usage:
- xor cx,cx
- mov si,msg_usage
- jmp fail
-
-too_long:
- xor cx,cx
- mov si,msg_too_long
- jmp fail
-
-parse_ip:
- MARK 'PIP'
- mov di,si
- mov si,pspCmdArg+1
- call parse_dotquad
- jc .notdq
- cmp si,di ; is it the same place?
- jne .notdq
- mov [tftp_siaddr],eax
- jmp gotprefix
-.notdq:
- MARK 'NDQ'
- mov si,di
- mov bx,pspCmdArg+1
- mov ax,0x0010 ; DNS resolve
- int 0x22
- and eax,eax
- jz noip
- mov [tftp_siaddr],eax
-gotprefix:
- MARK 'GOTP'
- dec cx ; skip the ::
- dec cx
- inc si
- inc si
- mov di,tftp_filename
- mov bx,128
-
-prefix_done:
- SHOWD bl
- MARK 'LEFT'
-
-; SI points at the filename, plus remaining arguments,
-; CX contains their combined length.
-; DI points to where the filename should be stored
-; BX says how much space is left for the filename and NUL
-
- and cx,cx
- jz usage ; no args is bad
-.copy_filename:
- lodsb
-%ifdef DEBUG
- mov dl,al
- mov ah,0x2
- int 0x21
-%endif
- cmp al,' '
- je usage
- dec bx
- jz too_long
- stosb
- loop .copy_filename
- xor eax,eax
- stosb
-
-; get PXE cached data
- MARK 'GCI'
- mov ax,0x0009 ; call PXE stack
- mov bx,0x0071 ; PXENV_GET_CACHED_INFO
- mov di,PXECacheParms
- int 0x22
- and eax,eax
- jz .fix_siaddr
- mov cx,[gci_status]
- mov si,msg_get_cache
- jmp fail
-
-.fix_siaddr:
- mov bx,[gci_bufferseg]
- mov es,bx
- mov bx,[gci_buffer]
- mov eax,[es:bx+12] ; save our address (ciaddr)
- mov [open_ciaddr],eax ; ... in case we have to do UDP open
- mov eax,[tftp_siaddr]
- and eax,eax
- jnz .replace_addr
- MARK 'ADDR'
- mov eax,[es:bx+20] ; siaddr
- mov [tftp_siaddr],eax
- jmp .addr_done
-.replace_addr:
- mov [es:bx+20],eax
-.addr_done:
- mov si,tftp_filename ; copy the new filename...
- lea di,[es:bx+108] ; to the "cached DHCP response"
- mov cx,128
- rep movsb
- mov bx,ds ; restore es before proceeding
- mov es,bx
-
-; print out what we are doing
-%ifdef DEBUG
- mov ah,0x02 ; write character
- mov dl,0x0d ; print a CRLF first
- int 0x21
- mov dl,0x0a
- int 0x21
-%endif
- mov ax,0x0002 ; write string
- mov bx,msg_booting
- int 0x22
- mov ebx,[tftp_siaddr]
- call print_dotquad
- mov ah,0x02 ; write character
- mov dl,' '
- int 0x21
- mov ax,0x0002 ; write string
- mov bx,tftp_filename
- int 0x22
- mov ah,0x02 ; write character
- mov dl,0x0d
- int 0x21
- mov dl,0x0a
- int 0x21
-
-%ifndef NO_RUN
- mov ax,0x0009 ; call PXE stack
- mov bx,0x0031 ; PXENV_UDP_CLOSE
- mov di,PXECloseParms
- int 0x22
- mov cx,[close_status]
- mov si,msg_udp_close
- and ax,ax
- jnz fail
-
- mov ax,0x0009 ; call PXE stack
- mov bx,0x0073 ; PXENV_RESTART_TFTP
- mov di,PXERestartTFTPParms
- int 0x22
- mov cx,[tftp_status]
- mov si,msg_rst_tftp
- call fail
-
- mov ax,0x0009 ; call PXE stack
- mov bx,0x0030 ; PXENV_UDP_OPEN
- mov di,PXEOpenParms
- int 0x22
- mov cx,[open_status]
- mov si,msg_udp_open
- and ax,ax
- jnz fail
- ret
-%endif
-
-fail:
- MARK 'FAIL'
- SHOWX cs
- SHOWX ds
- SHOWX es
- SHOWX si
-%ifdef DEBUG
- mov ah,0x02 ; write character
- mov dl,0x0d ; print a CRLF first
- int 0x21
- mov dl,0x0a
- int 0x21
-%endif
- mov ax,0x0002 ; write string
- mov bx,msg_progname ; print our name
- int 0x22
- mov bx,si ; ... the error message
- int 0x22
- mov ah,0x02 ; write character
- jcxz .done
- mov dl,' ' ; ... and the error code, in []
- int 0x21
- mov dl,'['
- int 0x21
- mov bx,cx
- call print_hex
- mov ah,0x02 ; write character
- mov dl,']'
- int 0x21
-.done:
- mov dl,0x0d ; and finally a CRLF
- int 0x21
- mov dl,0x0a
- int 0x21
- ret
-
-
-; print_hex
-;
-; Take a 16-bit integer in BX and print it as 2 hex digits.
-; Destroys AX and DL.
-;
-print_hex:
- mov al,bh
- aam 16
- cmp ah,10
- jb .lt_a000
- add ah,'A'-'0'-10
-.lt_a000: add ah,'0'
- mov dl,ah
- mov ah,0x02 ; write character
- int 0x21
-
- cmp al,10
- jb .lt_a00
- add al,'A'-'0'-10
-.lt_a00: add al,'0'
- mov dl,al
- mov ah,0x02 ; write character
- int 0x21
-
- mov al,bl
- aam 16
- cmp ah,10
- jb .lt_a0
- add ah,'A'-'0'-10
-.lt_a0: add ah,'0'
- mov dl,ah
- mov ah,0x02 ; write character
- int 0x21
-
- cmp al,10
- jb .lt_a
- add al,'A'-'0'-10
-.lt_a: add al,'0'
- mov dl,al
- mov ah,0x02 ; write character
- int 0x21
- ret
-
-
-; print_dec
-;
-; Take an 8-bit integer in AL and print it in decimal.
-; Destroys AX and DL.
-;
-print_dec:
- cmp al,10 ; < 10?
- jb .lt10 ; If so, skip first 2 digits
-
- cmp al,100 ; < 100
- jb .lt100 ; If so, skip first digit
-
- aam 100
- ; Now AH = 100-digit; AL = remainder
- add ah,'0'
- mov dl,ah
- mov ah,0x02
- int 0x21
-
-.lt100:
- aam 10
- ; Now AH = 10-digit; AL = remainder
- add ah,'0'
- mov dl,ah
- mov ah,0x02
- int 0x21
-
-.lt10:
- add al,'0'
- mov dl,al
- mov ah,0x02
- int 0x21
- ret
-
-
-; print_dotquad
-;
-; Take an IP address (in network byte order) in EBX and print it
-; as a dotted quad.
-; Destroys EAX, EBX, ECX, EDX
-;
-print_dotquad:
- mov cx,3
-.octet:
- mov al,bl
- call print_dec
- jcxz .done
- mov ah,0x02
- mov dl,'.'
- int 0x21
- ror ebx,8 ; Move next char into LSB
- dec cx
- jmp .octet
-.done:
- ret
-
-
-; parse_dotquad:
-; Read a dot-quad pathname in DS:SI and output an IP
-; address in EAX, with SI pointing to the first
-; nonmatching character.
-;
-; Return CF=1 on error.
-;
-; No segment assumptions permitted.
-;
-parse_dotquad:
- push cx
- mov cx,4
- xor eax,eax
-.parseloop:
- mov ch,ah
- mov ah,al
- lodsb
- sub al,'0'
- jb .notnumeric
- cmp al,9
- ja .notnumeric
- aad ; AL += 10 * AH; AH = 0;
- xchg ah,ch
- jmp .parseloop
-.notnumeric:
- cmp al,'.'-'0'
- pushf
- mov al,ah
- mov ah,ch
- xor ch,ch
- ror eax,8
- popf
- jne .error
- loop .parseloop
- jmp .done
-.error:
- loop .realerror ; If CX := 1 then we're done
- clc
- jmp .done
-.realerror:
- stc
-.done:
- dec si ; CF unchanged!
- pop cx
- ret
-
- section .data
-msg_booting: db 'TFTP boot: ',0
-msg_progname: db 'pxechain: ',0
-msg_usage: db 'usage: pxechain.cbt [[ipaddress]::]filename',0dh,0ah,0
-msg_too_long: db 'pxechain: filename is too long (max 127)',0dh,0ah,0
-msg_get_cache: db 'PXENV_GET_CACHED_INFO',0
-msg_rst_tftp: db 'PXENV_RESTART_TFTP',0
-msg_udp_close: db 'PXENV_UDP_CLOSE',0
-msg_udp_open: db 'PXENV_UDP_OPEN',0
-
-PXECacheParms:
-gci_status: dw 0
-gci_packettype: dw 3 ; PXENV_PACKET_TYPE_CACHED_REPLY
-gci_buffersize: dw 0
-gci_buffer: dw 0
-gci_bufferseg: dw 0
-gci_bufferlim: dw 0
-
-PXERestartTFTPParms:
-tftp_status: dw 0
-tftp_filename: times 128 db 0
-tftp_bufsize: dd 0x00090000 ; available memory for NBP
-tftp_bufaddr: dd 0x00007c00 ; PXE NBP load address
-tftp_siaddr: dd 0
-tftp_giaddr: dd 0
-tftp_mcaddr: dd 0
-tftp_mcport: dw 0
-tftp_msport: dw 0
-tftp_timeout: dw 0
-tftp_reopendly: dw 0
-
-PXECloseParms:
-close_status: dw 0
-
-PXEOpenParms:
-open_status: dw 0
-open_ciaddr: dd 0
diff --git a/modules/ver.asm b/modules/ver.asm
deleted file mode 100644
index 8ef63faf..00000000
--- a/modules/ver.asm
+++ /dev/null
@@ -1,606 +0,0 @@
-; ****************************************************************************
-;
-; ver.asm
-;
-; A COMBOOT/DOS COM program to display the version of the system
-; (Syslinux, DOS, or DRMK)
-;
-; Copyright (C) 2009-2010 Gene Cumm
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-; Boston MA 02111-1307, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; ****************************************************************************
-
-; %define DEBUG
-
- section .text
- org 0x100
-
-_start:
- call crlf
- mov si,info_str
- call writestr
- call getdosver
- call chkprn_dosver
- jnz .end
- call chkprn_syslinux
- call crlf
-.end:
-; pop ds
- ret
-
-
-; chkprn_syslinux
-chkprn_syslinux:
-%ifdef DEBUG
- mov si,may_sysl_str
- call writestr
-%endif
- cmp eax,59530000h
- jne .end
- cmp ebx,4C530000h
- jne .end
- cmp ecx,4E490000h
- jne .end
- cmp edx,58550000h
- jne .end
-.is_syslinux:
- pushad
-%ifdef DEBUG
- mov si,is_sysl_str
- call writestr
-%endif
-.get_sysl_ver:
- mov ax,0001h
- int 22h
-; AX=0001h [2.00] Get Version
-;
-; Input: AX 0001h
-; Output: AX number of INT 22h API functions available
-; CH Syslinux major version number
-; CL Syslinux minor version number
-; DL Syslinux derivative ID (e.g. 32h = PXELINUX)
-; ES:SI Syslinux version string
-; ES:DI Syslinux copyright string
-%ifdef DEBUG
- push si
- push cs
- pop ds
- mov si,gotver_str
- call writestr
- pop si
-%endif
-
-.prn_ver_str:
- mov si,syslban_str
- call writestr
- push ds
- push es
- pop ds
- call writestr
- call crlf
- pop ds
-.prn_var:
- cmp dl,31h
- je .var_sysl
- cmp dl,32h
- je .var_pxel
- cmp dl,33h
- je .var_isol
- cmp dl,34h
- je .var_extl
- jmp .var_unk
-.var_sysl:
- mov si,sysl_str
- call writestr
- jmp .prn_lnxsp
-.var_pxel:
- mov si,pxel_str
- call writestr
- jmp .prn_lnxsp
-.var_isol:
- mov si,isol_str
- call writestr
- jmp .prn_lnxsp
-.var_extl:
- mov si,extl_str
- call writestr
-; jmp .prn_lnxsp
-.prn_lnxsp:
- mov si,linsp_str
- call writestr
- jmp .prn_ver
-.var_unk:
- mov si,unkvar_str
- call writestr
-.prn_ver:
-%ifdef DEBUG
- push si
- push cs
- pop ds
- mov si,prn_ver_str
- call writestr
- pop si
-%endif
-.prn_ver_maj:
- mov al,ch
- call writedecb
- mov dl,'.'
- call writechr_dl
-.prn_ver_min:
- mov al,cl
-; cmp al,10
-; jae .min_wri
-; mov al,'0'
-; call writechr
-; mov al,cl
-; .min_wri:
-; call writedecb
- call writedecb2
-
-.end_prn:
- popad
-.end:
- ret
-
-; chkprn_dosver Check and print DOS version;
-; Input Data from INT21 AH=30h
-; AH Major version of DOS or 0
-; AL Minor Version
-; BH DOS type
-; BL:CX 24-bit OEM serial number
-; Return
-; ZF Unset if DOS, Set if not DOS (AX=0)
-chkprn_dosver:
- and ax,ax ; cmp ax,0
- jz .end
-.is_dos:
- push eax
- push edx
- push si
-%ifdef DEBUG
- mov si,is_dos_str
- call writestr
- call crlf
- call prnreg_gp_l
- call crlf
-%endif
-.var_prn:
- cmp bh,0
- je .var_pcdos
- cmp bh,0FFh
- je .var_msdos
- cmp bh,0FDh
- je .var_freedos
- cmp bh,0DEh
- je .var_drmk
- jmp .var_unk
-.var_pcdos:
- mov si,pcdos_str
- call writestr
- jmp .var_end
-.var_msdos:
- mov si,msdos_str
- call writestr
- jmp .var_end
-.var_freedos:
- mov si,freedos_str
- call writestr
- jmp .var_end
-.var_drmk:
- mov si,drmk_str
- call writestr
- jmp .var_end
-.var_unk:
- mov si,unkdos_str
- call writestr
- mov si,spparen_str
- call writestr
- push eax
- mov al,bh
- call writehex2
- pop eax
- mov si,parensp_str
- call writestr
-; jmp .var_end
-.var_end:
- call prn_dosver_num
- call crlf
-.subver:
- pop si
- pop edx
- pop eax
- cmp bh,0FFh
- je .msdos_ver
- cmp bh,0DEh
- jne .end_ver
-.drmk_ver:
- call getprn_drmkver
-; jmp .end_ver ; DRMK returns Extended/True DOS
-.msdos_ver:
- cmp al,5
- jb .end_ver
- call getprn_msdosver
-.end_ver:
- and ax,ax ; Unset ZF
-.end:
- ret
-
-; prn_dosver_num Print the numerical DOS version
-; Input Data from INT21 AH=30h
-; AH Major version of DOS or 0
-; AL Minor Version
-; BH DOS type
-; BL:CX 24-bit OEM serial number
-prn_dosver_num:
- push eax
- push edx
- push si
- pushfd
-.vmaj_prn:
- call writedecb
-; call writehex2
- mov dl,'.'
- call writechr_dl
-.vmin_prn:
- mov al,ah
- call writedecb
-; call writehex2
-.serial: ; Skip if 0
- cmp bl,0
- jne .ser_start
- cmp cx,0
- je .end
-.ser_start:
- mov si,spparen_str
- call writestr
- mov si,zerox_str
- call writestr
-.ser_bl:
- mov al,bl
- call writehex2
-.ser_cx:
- mov ax,cx
- call writehex4
-.serial_end:
- mov si,parensp_str
- call writestr
-.end:
- popfd
- pop si
- pop edx
- pop eax
- ret
-
-; getdosver Get the DOS version
-; Return Version or 0 + SYSLINUX message
-; EAX Part 1
-; EBX Part 2
-; ECX Part 3
-; EDX Part 4
-getdosver:
- mov ecx,0
- mov edx,0
- mov ebx,0
- mov eax,3000h
- int 21h
- ret
-
-; getmsdosver Get the Extended MS-DOS version
-; Returns Version
-; EAX Part 1
-; EBX Part 2
-; ECX Part 3
-; EDX Part 4
-getmsdosver:
- mov ecx,0
- mov edx,0
- mov ebx,0
- mov eax,3306h
- int 21h
- ret
-
-; getprn_msdosver
-getprn_msdosver:
- pushad
- pushfd
- call getmsdosver
-%ifdef DEBUG
- call prnreg_gp_l
- call crlf
-%endif
- mov si,dosext_str
- call writestr
- mov eax,ebx
- mov ebx,0
- mov ecx,edx
- call prn_dosver_num
-.end:
- popfd
- popad
- ret
-
-; getdrmkver: Get the DRMK-specifc OS version
-; Returns Version
-; AX OS Version
-; DX Patch Version
-getdrmkver:
- mov ax,4452h
- int 21h
- ret
-
-; getdrmkver: Get the DRMK-specifc Kernel build info
-; Returns Kernel build info
-; AX Kernel build date in DOS 16-bit format
-; [ES:BX] Kernel private data
-getdrmkbld:
- mov ax,4458h
- int 21h
- ret
-
-; getprn_drmkver: Get/Print DRMK-specific Version info
-getprn_drmkver:
- pushad
- pushfd
-.getver:
- call getdrmkver
-.prnosver: ; "OS Version"
- mov si,osver_str
- call writestr
- mov si,zerox_str
- call writestr
-; mov ax,0
- call writehex4
- call crlf
-.prnpatchver: ; "Patch Version"
- mov si,patchver_str
- call writestr
- mov si,zerox_str
- call writestr
- mov ax,dx
- call writehex4
- call crlf
-.getbld:
- call getdrmkbld
-.prnkernbld: ; "Kernel Build Date"
- mov si,kernbld_str
- call writestr
- call writedate_ax
- call crlf
-.prnkernprvaddr:
- mov si,prvdat_str
- call writestr
- mov ax,es
- call writehex4
- mov dl,':'
- call writechr_dl
- mov ax,bx
- call writehex4
- call crlf
-%ifdef DEBUG
-.prnkernprv:
- mov di,[es:bx]
- mov ax,di
- call writehex4
- call crlf
- mov si,2
- mov cx,8
-.prnkernprv2:
- push cx
- mov cx,8
-.prnkernprv1:
- mov eax,[es:bx+si]
- call writehex8
- cmp cx,1
- jbe .prnkern0dash
- mov ax,'-'
- call writechr
-.prnkern0dash:
- add si,4
- sub di,4
- cmp di,0
- jbe .prnkernprvend
- loop .prnkernprv1
- call crlf
- pop cx
- loop .prnkernprv2
- jmp .end
-.prnkernprvend:
- pop cx
-%endif
-.end:
- popfd
- popad
- ret
-
-;writedate_ax Write a date in AX in ISO8601 big endian format
-; Input
-; AX Date in 16-bit DOS format
-; 2006-01-11
-; 0011010 0001 01011
-writedate_ax:
- pushad
- pushfd
- mov dx,ax
-%ifdef DEBUG
- call writehex4
- call crlf
-%endif
-.year:
- shr ax,9
- add ax,1980
- call writedecw
- mov al,'-'
- call writechr
- mov ax,dx
-.month:
- shr ax,5
- and ax,0Fh
-; cmp ax,10
-; jae .month_wri
-; mov cx,ax
-; mov ax,'0'
-; call writechr
-; mov ax,cx
-; .month_wri:
-; call writedecb
- call writedecb2
- mov al,'-'
- call writechr
- mov ax,dx
-.day:
- and ax,1Fh
-; cmp ax,10
-; jae .day_wri
-; mov cx,ax
-; mov ax,'0'
-; call writechr
-; mov ax,cx
-; .day_wri:
-; call writedecb
- call writedecb2
-.end:
- popfd
- popad
- ret
-
-; writechr_dl Write a character to the console saving AX
-; Input
-; DL character to write
-writechr_dl:
- push ax
- mov ah,02h
- int 21h
-.end:
- pop ax
- ret
-
-; writechr_al Write a character to the console saving AX
-; Input
-; AL character to write
-writechr:
-writechr_al:
- push dx
- mov dl,al
- call writechr_dl
-.end: pop dx
- ret
-
-; writedecb[23] Print byte as fixed width
-; Input
-; AL number to write
-writedecb3:
- pushfd
- cmp al,100
- jae .skip
- push ax
- mov ax,'0'
- call writechr
- pop ax
-.skip: popfd
-writedecb2:
- pushfd
- cmp al,10
- jae .skip
- push ax
- mov ax,'0'
- call writechr
- pop ax
-.skip: popfd
- call writedecb
- ret
-
-
-; prnreg_gp_l Dump GP registers (Long)
-prnreg_gp_l:
- push eax
- push si
- call crlf
- mov si,sp2_str
- call writestr
- mov si,eax_str
- call writestr
- call writehex8
- mov si,sp2_str
- call writestr
- mov si,ecx_str
- call writestr
- mov eax,ecx
- call writehex8
- mov si,sp2_str
- call writestr
- mov si,edx_str
- call writestr
- mov eax,edx
- call writehex8
- mov si,sp2_str
- call writestr
- mov si,ebx_str
- call writestr
- mov eax,ebx
- call writehex8
- call crlf
- pop si
- pop eax
-.end:
- ret
-
-; is_zf
-is_zf:
- push si
- jz .true
-.false:
- mov si,zero_not_str
- call writestr
- jmp .end
-.true:
- mov si,zero_is_str
- call writestr
-.end:
- pop si
- ret
-
-%include "../core/macros.inc" ; CR/LF
-%include "../core/writestr.inc" ; String output
-%include "../core/writehex.inc" ; Hexadecimal output
-%include "../core/writedec.inc" ; Decimal output
-
- section .data
-info_str db 'Ver.com b026', CR, LF, 0
-is_dos_str db 'Found DOS', CR, LF, 0
-is_sysl_str db 'Found a Syslinux variant', CR, LF, 0
-is_drmk_str db 'Found DRMK', CR, LF, 0
-may_sysl_str db 'Maybe Syslinux variant', CR, LF, 0
-gotver_str db 'Got the version back', CR, LF, 0
-prn_ver_str db 'Printing version number', CR, LF, 0
-syslban_str db 'Syslinux banner: ',0
-sysl_str db 'SYS', 0
-pxel_str db 'PXE', 0
-isol_str db 'ISO', 0
-extl_str db 'EXT', 0
-linsp_str db 'LINUX ', 0
-unkvar_str db 'Unkown-Variant ', 0
-pcdos_str db 'PC-DOS ', 0
-msdos_str db 'MS-DOS ', 0
-freedos_str db 'FreeDOS ', 0
-unkdos_str db 'Unknown-DOS ', 0
-drmk_str db 'DRMK ', 0
-dosext_str db ' Extended DOS version: ', 0
-osver_str db ' OS Version: ', 0
-patchver_str db ' Patch Version: ', 0
-kernbld_str db ' Kernel Build Date: ', 0
-prvdat_str db ' Private Data Ptr: ', 0
-spparen_str db ' (', 0
-zerox_str db '0x', 0
-parensp_str db ') ', 0
-eax_str db 'EAX=', 0
-ebx_str db 'EBX=', 0
-ecx_str db 'ECX=', 0
-edx_str db 'EDX=', 0
-sp2_str db ' ', 0
-zero_not_str db ' NOT_Zero ',0
-zero_is_str db ' IS_Zero ',0
diff --git a/mtools/Makefile b/mtools/Makefile
index 78cea1e2..6df18b52 100755
--- a/mtools/Makefile
+++ b/mtools/Makefile
@@ -14,6 +14,7 @@ SRCS = syslinux.c \
../libinstaller/setadv.c \
../libinstaller/bootsect_bin.c \
../libinstaller/ldlinux_bin.c \
+ ../libinstaller/ldlinuxc32_bin.c \
$(wildcard ../libfat/*.c)
OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS)))
diff --git a/mtools/syslinux.c b/mtools/syslinux.c
index c65021bb..f43b5a5e 100755
--- a/mtools/syslinux.c
+++ b/mtools/syslinux.c
@@ -125,6 +125,64 @@ int libfat_xpread(intptr_t pp, void *buf, size_t secsize,
return xpread(pp, buf, secsize, offset);
}
+static int move_file(char *filename)
+{
+ char target_file[4096], command[5120];
+ char *cp = target_file, *ep = target_file + sizeof target_file - 16;
+ const char *sd;
+ int slash = 1;
+ int status;
+
+ cp += sprintf(cp, "'s:/");
+ for (sd = opt.directory; *sd; sd++) {
+ if (*sd == '/' || *sd == '\\') {
+ if (slash)
+ continue; /* Remove duplicated slashes */
+ slash = 1;
+ } else if (*sd == '\'' || *sd == '!') {
+ slash = 0;
+ if (cp < ep)
+ *cp++ = '\'';
+ if (cp < ep)
+ *cp++ = '\\';
+ if (cp < ep)
+ *cp++ = *sd;
+ if (cp < ep)
+ *cp++ = '\'';
+ continue;
+ } else {
+ slash = 0;
+ }
+
+ if (cp < ep)
+ *cp++ = *sd;
+ }
+ if (!slash)
+ *cp++ = '/';
+ sprintf(cp, "%s'", filename);
+
+ /* This command may fail legitimately */
+ sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file);
+ status = system(command);
+ (void)status; /* Keep _FORTIFY_SOURCE happy */
+
+ sprintf(command, "mmove -D o -D O s:/%s %s", filename, target_file);
+ status = system(command);
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ fprintf(stderr,
+ "%s: warning: unable to move %s\n", program, filename);
+
+ sprintf(command, "mattrib +r +h +s s:/%s", filename);
+ status = system(command);
+ } else {
+ sprintf(command, "mattrib +r +h +s %s", target_file);
+ status = system(command);
+ }
+
+ return status;
+}
+
int main(int argc, char *argv[])
{
static unsigned char sectbuf[SECTOR_SIZE];
@@ -284,63 +342,38 @@ int main(int argc, char *argv[])
/* Move ldlinux.sys to the desired location */
if (opt.directory) {
- char target_file[4096], command[5120];
- char *cp = target_file, *ep = target_file + sizeof target_file - 16;
- const char *sd;
- int slash = 1;
-
- cp += sprintf(cp, "'s:/");
- for (sd = opt.directory; *sd; sd++) {
- if (*sd == '/' || *sd == '\\') {
- if (slash)
- continue; /* Remove duplicated slashes */
- slash = 1;
- } else if (*sd == '\'' || *sd == '!') {
- slash = 0;
- if (cp < ep)
- *cp++ = '\'';
- if (cp < ep)
- *cp++ = '\\';
- if (cp < ep)
- *cp++ = *sd;
- if (cp < ep)
- *cp++ = '\'';
- continue;
- } else {
- slash = 0;
- }
-
- if (cp < ep)
- *cp++ = *sd;
- }
- if (!slash)
- *cp++ = '/';
- strcpy(cp, "ldlinux.sys'");
+ status = move_file("ldlinux.sys");
+ } else {
+ status = system("mattrib +r +h +s s:/ldlinux.sys");
+ }
- /* This command may fail legitimately */
- sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file);
- status = system(command);
- (void)status; /* Keep _FORTIFY_SOURCE happy */
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ fprintf(stderr,
+ "%s: warning: failed to set system bit on ldlinux.sys\n",
+ program);
+ }
- sprintf(command, "mmove -D o -D O s:/ldlinux.sys %s", target_file);
- status = system(command);
+ /* This command may fail legitimately */
+ status = system("mattrib -h -r -s s:/ldlinux.c32 2>/dev/null");
+ (void)status; /* Keep _FORTIFY_SOURCE happy */
- if (!WIFEXITED(status) || WEXITSTATUS(status)) {
- fprintf(stderr,
- "%s: warning: unable to move ldlinux.sys\n", program);
+ mtp = popen("mcopy -D o -D O -o - s:/ldlinux.c32", "w");
+ if (!mtp || fwrite(syslinux_ldlinuxc32, 1, syslinux_ldlinuxc32_len, mtp)
+ != syslinux_ldlinuxc32_len ||
+ (status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status))) {
+ die("failed to create ldlinux.c32");
+ }
- status = system("mattrib +r +h +s s:/ldlinux.sys");
- } else {
- sprintf(command, "mattrib +r +h +s %s", target_file);
- status = system(command);
- }
+ /* Move ldlinux.c32 to the desired location */
+ if (opt.directory) {
+ status = move_file("ldlinux.c32");
} else {
- status = system("mattrib +r +h +s s:/ldlinux.sys");
+ status = system("mattrib +r +h +s s:/ldlinux.c32");
}
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
fprintf(stderr,
- "%s: warning: failed to set system bit on ldlinux.sys\n",
+ "%s: warning: failed to set system bit on ldlinux.c32\n",
program);
}
diff --git a/txt/.gitignore b/txt/.gitignore
new file mode 100644
index 00000000..55c60417
--- /dev/null
+++ b/txt/.gitignore
@@ -0,0 +1,7 @@
+*.1
+*.5
+*.html
+*.pdf
+*.text
+*.xml
+docbook-xsl.css
diff --git a/txt/Makefile b/txt/Makefile
new file mode 100644
index 00000000..8ee80845
--- /dev/null
+++ b/txt/Makefile
@@ -0,0 +1,113 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2012 Gene Cumm
+##
+## Some logic from win32/Makefile:
+## Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
+## Copyright 2010 Intel Corporation; author: H. Peter Anvin
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## AsciiDoc documentation for syslinux
+##
+
+topdir = ..
+MAKEDIR = $(topdir)/mk
+# include $(MAKEDIR)/embedded.mk
+
+A2X_OPTS = -k
+# A2X_OPTS += -v
+A2X_MAN_OPTS = -D man -f manpage
+
+DOCS = syslinux.txt syslinux-cli.txt syslinux.cfg.txt \
+ isolinux.txt pxelinux.txt
+MAN_DOCS = man/syslinux.1 man/syslinux-cli.1 man/syslinux.cfg.5 \
+ man/isolinux.1 man/pxelinux.1
+HTML_DOCS := $(patsubst %.txt,html/%.html,$(DOCS))
+XHTML_DOCS := $(patsubst %.txt,%.html,$(DOCS))
+# MAN_DOCS := $(patsubst %.txt,man/%.1,$(DOCS1)) $(patsubst %.txt,man/%.5,$(DOCS5))
+TEXT_DOCS := $(patsubst %.txt,%.text,$(DOCS))
+PDF_DOCS := $(patsubst %.txt,%.pdf,$(DOCS))
+TARGETS =
+
+# ASCIIDOC_OK := $(shell which asciidoc 2>&1 ; echo $$?)
+ASCIIDOC_OK := $(shell which asciidoc > /dev/null ; echo $$?)
+A2X_XML_OK := $(shell a2x $(A2X_OPTS) -f docbook hello.txt 2>&1 ; echo $$?)
+ifeq ($(A2X_XML_OK),0)
+A2X_MAN_OK := $(shell [ ! -d man ] && mkdir man ; a2x $(A2X_MAN_OPTS) hello.txt 2>&1 ; echo $$?)
+A2X_XHTML_OK := $(shell a2x $(A2X_OPTS) -f xhtml hello.xml 2>&1 ; echo $$?)
+A2X_TEXT_OK := $(shell a2x $(A2X_OPTS) -f text hello.xml 2>&1 ; echo $$?)
+endif
+
+ifeq ($(ASCIIDOC_OK),0)
+TARGETS += $(HTML_DOCS)
+endif
+ifeq ($(A2X_MAN_OK),0)
+TARGETS += $(MAN_DOCS)
+endif
+ifeq ($(A2X_XHTML_OK),0)
+TARGETS += $(XHTML_DOCS)
+endif
+ifeq ($(A2X_TEXT_OK),0)
+TARGETS += $(TEXT_DOCS)
+endif
+
+# $(HTML_DOCS) $(MAN_DOCS) $(XHTML_DOCS) $(TEXT_DOCS)
+all: $(TARGETS)
+
+syslinux.cfg.txt: com-bug.txt com-rpt.txt
+
+# During 'make all', *.xml is kept but deleted at the end; do we _really_
+# need the XML longer?
+.PRECIOUS: %.xml
+
+# %.html: %.txt
+# asciidoc -D html $<
+
+html/ man/ text/ xhtml/:
+ mkdir $@
+
+html/%.html: %.txt | html/
+ asciidoc -o $@ $<
+
+# As of AsciiDoc-8.5.2, altering the output filename for a2x does not appear possible
+# xhtml/%.html: %.txt
+# a2x $(A2X_OPTS) -D xhtml -f xhtml $<
+# text/%.text: %.xml %.txt
+# a2x $(A2X_OPTS) -D text -f text $<
+
+%.xml: %.txt
+ a2x $(A2X_OPTS) -f docbook $<
+
+# when %.xml is missing, an update to %.txt must force regeneration
+%.html: %.xml %.txt
+ a2x $(A2X_OPTS) -f xhtml $<
+
+man/%.1: %.txt | man/
+ a2x $(A2X_MAN_OPTS) $<
+
+man/%.5: %.txt | man/
+ a2x $(A2X_MAN_OPTS) $<
+
+%.text: %.xml %.txt
+ a2x $(A2X_OPTS) -f text $<
+
+%.pdf: %.xml %.txt
+ a2x $(A2X_OPTS) -f pdf $<
+
+tidy dist:
+ rm -f *~ *.xml *.text.html text/*.text.html text/*.xml xhtml/*.xml
+
+clean: tidy
+
+spotless: clean
+ rm -f *.1 *.5 *.css *.html *.text html/*.html man/*.1 man/*.5 text/*.text xhtml/*.html xhtml/*.css
+
+-include .*.d
diff --git a/txt/com-bug.txt b/txt/com-bug.txt
new file mode 100644
index 00000000..f16139a0
--- /dev/null
+++ b/txt/com-bug.txt
@@ -0,0 +1,11 @@
+
+// == KNOWN BUGS ==
+
+Several known bugs/common problems are listed at
+http://www.syslinux.org/wiki/index.php/Common_Problems and known
+hardware compatibility issues are listed at
+http://www.syslinux.org/wiki/index.php/Hardware_Compatibility with
+filename translation difficulty and early PXE stacks being some of the
+most common. Reporting of other encountered issues is welcome and
+appreciated.
+
diff --git a/txt/com-derv.txt b/txt/com-derv.txt
new file mode 100644
index 00000000..21c70c79
--- /dev/null
+++ b/txt/com-derv.txt
@@ -0,0 +1,11 @@
+
+// == DERIVATIVES ==
+
+The *Syslinux* suite contains several boot loader (core) derivatives (variants), currently based solely on the boot media:
+
+ *SYSLINUX* - Disk (floppy/hard disk) based
+ *PXELINUX* - PXE network booting
+ *ISOLINUX* - ISO9660 (CD/DVD; El Torito) based
+
+Prior to v4.00, *SYSLINUX* was only for FAT12/FAT16/FAT32 and another derivative, *EXTLINUX*, was for ext2/ext3 file systems, whose functionality has been merged into *SYSLINUX*.
+
diff --git a/txt/com-name.txt b/txt/com-name.txt
new file mode 100644
index 00000000..b24811f2
--- /dev/null
+++ b/txt/com-name.txt
@@ -0,0 +1,12 @@
+
+// == NAMING CONVENTION ==
+
+As of 3.73, a standard for naming conventions was adopted to more
+clearly distinguish different aspects. The moniker "The *Syslinux*
+Project", in standard proper noun capitalization, shall be used to refer
+to the project as a whole. The all-caps form shall be used to refer to a
+particular loader variant such as "*SYSLINUX*" for the standard disk
+(floppy/hard disk; formerly just FAT) loader. The all-lower case form
+should be used to refer to a particular installer such as
+"syslinux-mtools" for the mtools-based installer for *SYSLINUX*.
+
diff --git a/txt/com-rpt.txt b/txt/com-rpt.txt
new file mode 100644
index 00000000..fe368eee
--- /dev/null
+++ b/txt/com-rpt.txt
@@ -0,0 +1,22 @@
+
+// == BUG REPORTS ==
+
+I would appreciate hearing of any problems you have with Syslinux. I
+would also like to hear from you if you have successfully used Syslinux,
+*especially* if you are using it for a distribution.
+
+If you are reporting problems, please include all possible information
+about your system and your BIOS; the vast majority of all problems
+reported turn out to be BIOS or hardware bugs, and I need as much
+information as possible in order to diagnose the problems.
+
+There is a mailing list for discussion among Syslinux users and for
+announcements of new and test versions. To join, or to browse the
+archive, go to:
+
+http://www.zytor.com/mailman/listinfo/syslinux
+
+Please DO NOT send HTML messages or attachments to the mailing list
+(including multipart/alternative or similar.) All such messages will be
+bounced.
+
diff --git a/txt/hello.txt b/txt/hello.txt
new file mode 100644
index 00000000..37576277
--- /dev/null
+++ b/txt/hello.txt
@@ -0,0 +1,16 @@
+= hello(1) =
+:doctype: manpage
+:author: World Greeter
+:author-email: hello@domain.local
+:revdate: 1970-01-01
+:data-uri:
+
+
+== NAME ==
+hello - hello world
+
+== SYNOPSIS ==
+hello
+
+== Description ==
+Say hello and greet the world
diff --git a/txt/isolinux.txt b/txt/isolinux.txt
new file mode 100644
index 00000000..55e7d7c5
--- /dev/null
+++ b/txt/isolinux.txt
@@ -0,0 +1,116 @@
+= isolinux(1) =
+:doctype: manpage
+:revdate: 2013-06-12
+:author: H. Peter Anvin
+:author-email: hpa@zytor.com
+:editor1: Gene Cumm
+:editor1-email: gene.cumm@gmail.com
+:editor1-revlast: 2013-06-12
+
+
+== NAME ==
+isolinux - The Syslinux derivative ISOLINUX for ISO9660 CD/DVD media
+
+
+== SYNOPSIS ==
+[verse]
+*mkisofs* -o 'isoimage' \
+ -b 'isolinux/isolinux.bin' -c 'isolinux/boot.cat' \
+ -no-emul-boot -boot-load-size 4 -boot-info-table \
+ 'root-of-iso-tree'
+
+
+== DESCRIPTION ==
+ISOLINUX is a boot loader for Linux/i386 that operates off ISO 9660/El
+Torito CD-ROMs in "no emulation" mode. This avoids the need to create
+an "emulation disk image" with limited space (for "floppy emulation")
+or compatibility problems (for "hard disk emulation".)
+
+To create an image, create a directory called "isolinux/" (or, if you
+prefer, "boot/isolinux/") underneath the root directory of your ISO image
+master file tree. Copy isolinux.bin, a config file called
+"isolinux.cfg" (see *syslinux.cfg*(5) for details on the configuration file),
+and all necessary files (kernels, initrd, display files, etc.) into this
+directory, then use the above command to create your ISO image (add
+additional options as appropriate, such as -J or -R). If you named the
+directory boot/isolinux that should of course be +
+ -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat.
+
+
+== CONFIG FILE DIRECTORY ==
+
+ISOLINUX will search for the config file directory in the order
+/boot/isolinux, /isolinux, /. The first directory that exists is
+used, even if it contains no files. Therefore, please make sure that
+these directories don't exist if you don't want ISOLINUX to use them.
+
+
+== HYBRID CD-ROM/HARD DISK MODE ==
+
+Starting in version 3.72, ISOLINUX supports a "hybrid mode" which can
+be booted from either CD-ROM or from a device which BIOS considers a
+hard disk or ZIP disk, e.g. a USB key or similar.
+
+To enable this mode, the .iso image should be postprocessed with the
+"isohybrid" script from the utils directory:
+
+ isohybrid filename.iso
+
+This script creates the necessary additional information to be able to
+boot in hybrid mode. It also pads out the image to an even multiple
+of 1 MB.
+
+This image can then be copied using any raw disk writing tool (on Unix
+systems, typically "dd" or "cat") to a USB disk, or written to a
+CD-ROM using standard CD burning tools.
+
+The ISO 9660 filesystem is encapsulated in a partition (which starts
+at offset zero, which may confuse some systems.) This makes it
+possible for the operating system, once booted, to use the remainder
+of the device for persistent storage by creating a second partition.
+
+
+== MISCELLANEOUS ==
+Make sure you have a recent enough version of mkisofs. I recommend
+mkisofs 1.13 (distributed with cdrecord 1.9), but 1.12 might work as
+well (not tested.)
+
+ISOLINUX resolves pathnames the following way:
+
+- A pathname consists of names separated by slashes, Unix-style.
+- A leading / means it searches from the root directory; otherwise the
+ search is from the isolinux directory (think of this as the "current
+ directory".)
+- . and .. in pathname searches are not supported.
+- The maximum length of any pathname is 255 characters.
+
+Note that ISOLINUX only uses the "plain" ISO 9660 filenames, i.e. it
+does not support Rock Ridge or Joliet filenames. It can still be used
+on a disk which uses Rock Ridge and/or Joliet extensions, of course.
+Under Linux, you can verify the plain filenames by mounting with the
+"-o norock,nojoliet" option to the mount command. Note, however, that
+ISOLINUX does support "long" (level 2) ISO 9660 plain filenames, so if
+compatibility with short-names-only operating systems like MS-DOS is
+not an issue, you can use the "-l" or "-iso-level 2" option to mkisofs
+to generate long (up to 31 characters) plain filenames.
+
+ISOLINUX does not support discontiguous files, interleaved mode, or
+logical block and sector sizes other than 2048. This should normally
+not be a problem.
+
+ISOLINUX is by default built in two versions, one version with extra
+debugging messages enabled. If you are having problems with ISOLINUX,
+I would greatly appreciate if you could try out the debugging version
+(isolinux-debug.bin) and let me know what it reports. The debugging
+version does not include hybrid mode support (see below.)
+
+
+== SEE ALSO ==
+*syslinux.cfg*(5), *syslinux-cli*(1), *lilo*(8), *keytab-lilo.pl*(8),
+*fdisk*(8), *mkfs*(8), *superformat*(1).
+
+
+== AUTHOR ==
+This AsciiDoc derived document is a modified version of the original
+*SYSLINUX* documentation by {author} <{author-email}>. The conversion
+to an AsciiDoc was made by {editor1} <{editor1-email}>
diff --git a/txt/pxelinux.txt b/txt/pxelinux.txt
new file mode 100644
index 00000000..77d34fdd
--- /dev/null
+++ b/txt/pxelinux.txt
@@ -0,0 +1,461 @@
+= pxelinux(1) =
+:doctype: manpage
+:revdate: 2013-06-12
+:author: H. Peter Anvin
+:author-email: hpa@zytor.com
+:editor1: Gene Cumm
+:editor1-email: gene.cumm@gmail.com
+:editor1-revlast: 2013-06-12
+
+
+== NAME ==
+pxelinux - The Syslinux derivative PXELINUX for PXE network booting
+
+
+== SYNOPSIS ==
+[verse]
+pxelinux.0
+
+
+== DESCRIPTION ==
+*PXELINUX* is a Syslinux derivative, for booting Linux off a network
+server, using a network ROM conforming to the Intel PXE (Pre-Execution
+Environment) specification. *PXELINUX* is _*not*_ a program that is
+intended to be flashed or burned into a PROM on the network card; if
+you want that, check out Etherboot (http://www.etherboot.org/).
+Etherboot 5.4 or later can also be used to create a PXE-compliant boot
+PROM for many network cards.
+//FIXME: Needs gPXE/iPXE note
+
+PXELINUX generally requires that full file pathnames are 127 characters or shorter in length.
+//FIXME: why? many tftpds limiting to 127+null? outdated?
+
+
+== CURRENT DIRECTORY ==
+The initial current working directory is either as supplied by DHCP
+option 210 (pxelinux.pathprefix), the hardcoded path-prefix or the
+parent directory of the PXELINUX file, as indicated by DHCP fields
+'sname' and 'file' (sname="192.168.2.3" and file="boot/pxelinux.0"
+results in "tftp://192.168.2.3/boot/", "192.168.2.3::boot/" in older
+PXELINUX format) with precedence specified under *OPTIONS*.
+
+All unqualified filenames are relative to the current directory.
+
+
+== CONFIGURATION ==
+See *syslinux.cfg*(5) for the format of the contents.
+
+Because more than one system may be booted from the same server, the
+configuration file name depends on the IP address of the booting
+machine. After attempting the file as specified in the DHCP or
+hardcoded options, PXELINUX will probe the following paths, prefixed
+with "pxelinux.cfg/", under the initial current working directory:
+
+- The client UUID if provided by the PXE stack (note, some BIOSes don't
+have a valid UUID, and you might end up with something like all 1's.)
+This is in the standard UUID format using lower case hexadecimal digits,
+e.g. b8945908-d6a6-41a9-611d-74a6ab80b83d.
+
+- The hardware type (using its ARP type code) and address, all in lower
+case hexadecimal with dash separators; for example, for an Ethernet (ARP
+type 1) with address 88:99:AA:BB:CC:DD it would search for the filename
+01-88-99-aa-bb-cc-dd.
+
+- The client's IPv4 address in upper-case hexidecimal (ie 192.168.2.91
+-> C0A8025B; you can use the included progam "gethostip" to compute the
+hexadecimal IP address for any host.) followed by removing characters,
+one at a time, from the end.
+
+- "default"
+
+Starting in release 3.20, if PXELINUX can not find a configuration file,
+it will reboot after the timeout interval has expired. This keeps a
+machine from getting stuck indefinitely due to a boot server failure.
+
+
+== OPTIONS ==
+*PXELINUX* (starting with version 1.62) supports the following
+nonstandard DHCP options, which depending on your DHCP server you may be
+able to use to customize the specific behaviour of *PXELINUX*. See RFC
+5071 for some additional information about these options. Options for
+*PXELINUX* can be specified by DHCP options or hardcoded into the
+binary.
+
+=== Option Priority ===
+Hardcoded after-options are applied after DHCP options (and overrride)
+while hardcoded before-options are applied prior to DHCP options and
+default behavior takes the lowest priority.
+
+=== DHCP options ===
+*Option 208* (pxelinux.magic)::
+Earlier versions of *PXELINUX* required this to be set to F1:00:74:7E
+(241.0.116.126) for *PXELINUX* to recognize any special DHCP options
+whatsoever. As of *PXELINUX* 3.55, this option is deprecated and is no
+longer required.
+
+*Option 209* (pxelinux.configfile)::
+Specifies the initial *PXELINUX* configuration file name which may be
+qualified or unqualified.
+
+*Option 210* (pxelinux.pathprefix)::
+Specifies the *PXELINUX* common path prefix, instead of deriving it from
+the boot file name. This almost certainly needs to end in whatever
+character the TFTP server OS uses as a pathname separator, e.g. slash
+(/) for Unix.
+
+*Option 211* (pxelinux.reboottime)::
+Specifies, in seconds, the time to wait before reboot in the event of
+TFTP failure. 0 means wait "forever" (in reality, it waits
+approximately 136 years.)
+
+=== Hardcoded options ===
+Since version 3.83, the program "pxelinux-options" can be used to
+hard-code DHCP options into the pxelinux.0 image file; this is
+sometimes useful when the DHCP server is under different
+administrative control. Hardcoded options
+
+ 6 => 'domain-name-servers',
+ 15 => 'domain-name',
+ 54 => 'next-server',
+ 209 => 'config-file',
+ 210 => 'path-prefix',
+ 211 => 'reboottime'
+
+
+== HTTP/FTP ==
+Since version 5.10, a special PXELINUX binary, lpxelinux.0, natively
+supports HTTP and FTP transfers, greatly increasing load speed and
+allowing for standard HTTP scripts to present PXELINUX's configuration
+file. To use http or ftp, use standard URL syntax as filename; use the
+DHCP options below to transmit a suitable URL prefix to the client, or
+use the "pxelinux-options" tool provided in the utils directory to
+program it directly into the lpxelinux.0 file.
+
+
+== FILENAME SYNTAX ==
+//FIXME
+PXELINUX supports the following special pathname conventions:
+
+*::filename*::
+Suppresses the common filename prefix, i.e. passes the string "filename"
+unmodified to the server.
+
+*IP address::filename* (e.g. 192.168.2.3::filename)::
+Suppresses the common filename prefix, *and* sends a request to an alternate TFTP server. Instead of an IP address, a DNS name can be used. It will be assumed to be fully qualified if it contains dots; otherwise the local domain as reported by the DHCP server (option 15) will be added.
+
+:: was chosen because it is unlikely to conflict with operating system
+usage. However, if you happen to have an environment for which the
+special treatment of :: is a problem, please contact the Syslinux
+mailing list.
+
+Since version 4.00, PXELINUX also supports standard URL syntax.
+
+
+== KEEPPXE ==
+Normally, PXELINUX will unload the PXE and UNDI stacks before invoking
+the kernel. In special circumstances (for example, when using MEMDISK
+to boot an operating system with an UNDI network driver) it might be
+desirable to keep the PXE stack in memory. If the option "keeppxe"
+is given on the kernel command line, PXELINUX will keep the PXE and
+UNDI stacks in memory. (If you don't know what this means, you
+probably don't need it.)
+
+
+== EXAMPLES ==
+
+=== Configuration filename ===
+For DHCP siaddr 192.168.2.3, file 'mybootdir/pxelinux.0', client UUID
+b8945908-d6a6-41a9-611d-74a6ab80b83d, Ethernet MAC address
+88:99:AA:BB:CC:DD and IPv4 address 192.168.2.91, the following files in
+this order will be attempted (after config-file options):
+
+ mybootdir/pxelinux.cfg/b8945908-d6a6-41a9-611d-74a6ab80b83d
+ mybootdir/pxelinux.cfg/01-88-99-aa-bb-cc-dd
+ mybootdir/pxelinux.cfg/C0A8025B
+ mybootdir/pxelinux.cfg/C0A8025
+ mybootdir/pxelinux.cfg/C0A802
+ mybootdir/pxelinux.cfg/C0A80
+ mybootdir/pxelinux.cfg/C0A8
+ mybootdir/pxelinux.cfg/C0A
+ mybootdir/pxelinux.cfg/C0
+ mybootdir/pxelinux.cfg/C
+ mybootdir/pxelinux.cfg/default
+
+
+=== TFTP servers ===
+For best results, use a TFTP server which supports the "tsize" TFTP
+option (RFC 1784/RFC 2349). The "tftp-hpa" TFTP server, which support
+options, is available at:
+
+ http://www.kernel.org/pub/software/network/tftp/
+ ftp://www.kernel.org/pub/software/network/tftp/
+
+and on any kernel.org mirror (see http://www.kernel.org/mirrors/).
+
+Another TFTP server which supports this is atftp by Jean-Pierre
+Lefebvre:
+
+ ftp://ftp.mamalinux.com/pub/atftp/
+
+If your boot server is running Windows (and you can't fix that), try
+tftpd32 by Philippe Jounin (you need version 2.11 or later; previous
+versions had a bug which made it incompatible with PXELINUX):
+
+ http://tftpd32.jounin.net/
+
+
+=== DHCP config: Simple ===
+The PXE protocol uses a very complex set of extensions to DHCP or
+BOOTP. However, most PXE implementations -- this includes all Intel
+ones version 0.99n and later -- seem to be able to boot in a
+"conventional" DHCP/TFTP configuration. Assuming you don't have to
+support any very old or otherwise severely broken clients, this is
+probably the best configuration unless you already have a PXE boot
+server on your network.
+
+A sample DHCP setup, using the "conventional TFTP" configuration,
+would look something like the following, using ISC dhcp 2.0 dhcpd.conf
+syntax:
+
+-----
+allow booting;
+allow bootp;
+
+# Standard configuration directives...
+
+option domain-name "<domain name>";
+option subnet-mask <subnet mask>;
+option broadcast-address <broadcast address>;
+option domain-name-servers <dns servers>;
+option routers <default router>;
+
+# Group the PXE bootable hosts together
+group {
+ # PXE-specific configuration directives...
+ next-server <TFTP server address>;
+ filename "/tftpboot/pxelinux.0";
+
+ # You need an entry like this for every host
+ # unless you're using dynamic addresses
+ host <hostname> {
+ hardware ethernet <ethernet address>;
+ fixed-address <hostname>;
+ }
+}
+-----
+
+Note that if your particular TFTP daemon runs under chroot (tftp-hpa
+will do this if you specify the -s (secure) option; this is highly
+recommended), you almost certainly should not include the /tftpboot
+prefix in the filename statement.
+
+
+=== DHCP Config: PXE-1 ===
+If the simple config does not work for your environment, you probably
+should set up a "PXE boot server" on port 4011 of your TFTP server; a
+free PXE boot server is available at:
+
+http://www.kano.org.uk/projects/pxe/
+
+With such a boot server defined, your DHCP configuration should look
+the same except for an "option dhcp-class-identifier" ("option
+vendor-class-identifier" if you are using DHCP 3.0):
+
+----
+allow booting;
+allow bootp;
+
+# Standard configuration directives...
+
+option domain-name "<domain name>";
+option subnet-mask <subnet mask>;
+option broadcast-address <broadcast address>;
+option domain-name-servers <dns servers>;
+option routers <default router>;
+
+# Group the PXE bootable hosts together
+group {
+ # PXE-specific configuration directives...
+ option dhcp-class-identifier "PXEClient";
+ next-server <pxe boot server address>;
+
+ # You need an entry like this for every host
+ # unless you're using dynamic addresses
+ host <hostname> {
+ hardware ethernet <ethernet address>;
+ fixed-address <hostname>;
+ }
+}
+----
+
+Here, the boot file name is obtained from the PXE server.
+
+
+=== DHCP Config: Encapsulated ===
+If the "conventional TFTP" configuration doesn't work on your clients,
+and setting up a PXE boot server is not an option, you can attempt the
+following configuration. It has been known to boot some
+configurations correctly; however, there are no guarantees:
+----
+allow booting;
+allow bootp;
+
+# Standard configuration directives...
+
+option domain-name "<domain name>";
+option subnet-mask <subnet mask>;
+option broadcast-address <broadcast address>;
+option domain-name-servers <dns servers>;
+option routers <default router>;
+
+# Group the PXE bootable hosts together
+group {
+ # PXE-specific configuration directives...
+ option dhcp-class-identifier "PXEClient";
+ option vendor-encapsulated-options 09:0f:80:00:0c:4e:65:74:77:6f:72:6b:20:62:6f:6f:74:0a:07:00:50:72:6f:6d:70:74:06:01:02:08:03:80:00:00:47:04:80:00:00:00:ff;
+ next-server <TFTP server>;
+ filename "/tftpboot/pxelinux.0";
+
+ # You need an entry like this for every host
+ # unless you're using dynamic addresses
+ host <hostname> {
+ hardware ethernet <ethernet address>;
+ fixed-address <hostname>;
+ }
+}
+----
+Note that this *will not* boot some clients that *will* boot with the
+"conventional TFTP" configuration; Intel Boot Client 3.0 and later are
+known to fall into this category.
+
+
+=== DHCP Config: ISC dhcpd options ===
+ISC dhcp 3.0 supports a rather nice syntax for specifying custom
+options; you can use the following syntax in dhcpd.conf if you are
+running this version of dhcpd:
+----
+option space pxelinux;
+option pxelinux.magic code 208 = string;
+option pxelinux.configfile code 209 = text;
+option pxelinux.pathprefix code 210 = text;
+option pxelinux.reboottime code 211 = unsigned integer 32;
+----
+ NOTE: In earlier versions of PXELINUX, this would only work as a
+ "site-option-space". Since PXELINUX 2.07, this will work both as a
+ "site-option-space" (unencapsulated) and as a "vendor-option-space"
+ (type 43 encapsulated.) This may avoid messing with the
+ dhcp-parameter-request-list, as detailed below.
+
+Then, inside your PXELINUX-booting group or class (whereever you have
+the PXELINUX-related options, such as the filename option), you can
+add, for example:
+----
+# Always include the following lines for all PXELINUX clients
+site-option-space "pxelinux";
+option pxelinux.magic f1:00:74:7e;
+if exists dhcp-parameter-request-list {
+ # Always send the PXELINUX options (specified in hexadecimal)
+ option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list,d0,d1,d2,d3);
+}
+# These lines should be customized to your setup
+option pxelinux.configfile "configs/common";
+option pxelinux.pathprefix "/tftpboot/pxelinux/files/";
+option pxelinux.reboottime 30;
+filename "/tftpboot/pxelinux/pxelinux.bin";
+----
+Note that the configfile is relative to the pathprefix: this will look
+for a config file called /tftpboot/pxelinux/files/configs/common on
+the TFTP server.
+
+The "option dhcp-parameter-request-list" statement forces the DHCP
+server to send the PXELINUX-specific options, even though they are not
+explicitly requested. Since the DHCP request is done before PXELINUX
+is loaded, the PXE client won't know to request them.
+
+Using ISC dhcp 3.0 you can create a lot of these strings on the fly.
+For example, to use the hexadecimal form of the hardware address as
+the configuration file name, you could do something like:
+----
+site-option-space "pxelinux";
+option pxelinux.magic f1:00:74:7e;
+if exists dhcp-parameter-request-list {
+ # Always send the PXELINUX options (specified in hexadecimal)
+ option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list,d0,d1,d2,d3);
+}
+option pxelinux.configfile =
+ concat("pxelinux.cfg/", binary-to-ascii(16, 8, ":", hardware));
+filename "/tftpboot/pxelinux.bin";
+----
+If you used this from a client whose Ethernet address was
+58:FA:84:CF:55:0E, this would look for a configuration file named
+"/tftpboot/pxelinux.cfg/1:58:fa:84:cf:55:e".
+
+
+== KNOWN ISSUES ==
+The following problems are known with PXELINUX, so far:
+
+- The error recovery routine doesn't work quite right. For right now,
+ it just does a hard reset - seems good enough.
+- We should probably call the UDP receive function in the keyboard
+ entry loop, so that we answer ARP requests.
+- Boot sectors/disk images are not supported yet.
+
+If you have additional problems, please contact the Syslinux mailing
+list (see syslinux.txt for the address.)
+
+=== Broken PXE stacks ===
+Lots of PXE stacks, especially old ones, have various problems of
+varying degrees of severity. Please see:
+
+ http://syslinux.zytor.com/hardware.php
+
+... for a list of currently known hardware problems, with workarounds
+if known.
+
+There are a number of extremely broken PXE stacks in the field. The
+gPXE project (formerly known as Etherboot) provides an open-source PXE
+stack that works with a number of cards, and which can be loaded from
+a CD-ROM, USB key, or floppy if desired.
+
+Information on gPXE is available from:
+
+ http://www.etherboot.org/
+
+... and ready-to-use ROM or disk images from:
+
+ http://www.rom-o-matic.net/
+
+Some cards, like may systems with the SiS 900, has a PXE stack which
+works just barely well enough to load a single file, but doesn't
+handle the more advanced items required by PXELINUX. If so, it is
+possible to use the built-in PXE stack to load gPXE, which can then
+load PXELINUX. See:
+
+ http://www.etherboot.org/wiki/pxechaining
+
+
+== NOTES ==
+=== MTFTP ===
+PXELINUX does not support MTFTP, and there are no plans of doing so, as
+MTFTP is inherently broken for files more than 65535 packets (about 92
+MB) in size. It is of course possible to use MTFTP for the initial
+boot, if you have such a setup. MTFTP server setup is beyond the scope
+of this document.
+
+=== Error Recovery ===
+If the boot fails, PXELINUX (unlike SYSLINUX) will not wait forever;
+rather, if it has not received any input for approximately five
+minutes after displaying an error message, it will reset the machine.
+This allows an unattended machine to recover in case it had bad enough
+luck of trying to boot at the same time the TFTP server goes down.
+
+
+== SEE ALSO ==
+*syslinux.cfg*(5), *syslinux-cli*(1), *lilo*(8), *keytab-lilo.pl*(8),
+*fdisk*(8), *mkfs*(8), *superformat*(1).
+
+
+== AUTHOR ==
+This AsciiDoc derived document is a modified version of the original
+*SYSLINUX* documentation by {author} <{author-email}>. The conversion
+to an AsciiDoc was made by {editor1} <{editor1-email}>
diff --git a/txt/syslinux-cli.txt b/txt/syslinux-cli.txt
new file mode 100644
index 00000000..774e8e27
--- /dev/null
+++ b/txt/syslinux-cli.txt
@@ -0,0 +1,93 @@
+= syslinux-cli(1) =
+:doctype: manpage
+:revdate: 2012-11-10
+:author: H. Peter Anvin
+:author-email: hpa@zytor.com
+:editor1: Gene Cumm
+:editor1-email: gene.cumm@gmail.com
+:editor1-revlast: 2012-11-10
+:data-uri:
+
+== NAME ==
+syslinux-cli - *Syslinux* boot prompt/command line interface
+
+
+== DESCRIPTION ==
+*Syslinux*'s boot prompt provides a very simplistic command line
+interface for loading modules and booting kernels.
+
+
+== BOOT PROMPT ==
+=== COMMAND LINE KEYSTROKES ===
+The command line prompt supports the following keystrokes:
+
+ <Enter> boot specified command line
+ <BackSpace> erase one character
+ <Ctrl-U> erase the whole line
+ <Ctrl-V> display the current Syslinux version
+ <Ctrl-W> erase one word
+ <Ctrl-X> force text mode
+ <Tab> list matching labels
+ <F1>..<F12> help screens (if configured)
+ <Ctrl-F><digit> equivalent to F1..F10
+ <Ctrl-C> interrupt boot in progress
+ <Esc> interrupt boot in progress
+ <Ctrl-N> display network information (PXELINUX only; 3.50-4.06)
+
+
+=== WORKING DIRECTORY ===
+At start, the initial working directory for *SYSLINUX*/*ISOLINUX* will
+be the directory containing the initial configuration file. If no
+configuration file is found, *SYSLINUX* should default to the
+install-time working directory, however this is a known issue with some
+versions including 4.06.
+
+At start, the initial working directory for *PXELINUX* will be the
+parent directory of pxelinux.0 unless overridden with DHCP option 210.
+If no configuration file is found, *PXELINUX* will start a timer to
+reboot the system in an attempt to restart the boot process and resolve
+a possible transient issue.
+
+
+=== ALTERNATE FILENAMES ===
+For kernel-like file names given on the command line, *Syslinux* will
+attempt to append file name extensions to the specified file name when
+the file is not found in the following order: .0[*PXELINUX* only],
+.bin[*ISOLINUX* only], .bs[*SYSLINUX* only], .bss[*SYSLINUX* only],
+.c32, .cbt[Up to 4.06], .com[Up to 4.06] and .img[*ISOLINUX* 1.65-4.04 only].
+
+// Is this true of file names specified in a config? As of when?
+
+
+=== PATH RULES ===
+
+The current working directory is *always* searched first, before PATH,
+when attempting to open a filename. The current working directory is
+not affected when specifying a file with an absolute path. For
+example, given the following file system layout,
+
+....
+/boot/
+ /bin/
+ ls.c32
+ libls.c32
+ /foo/
+ libls.c32
+....
+
+assuming that the current working directory is /boot/foo, and assuming
+that libls.c32 is a dependency of ls.c32, executing /boot/bin/ls.c32
+will cause /boot/foo/libls.c32 to be loaded, not /boot/bin/libls.c32,
+even if /boot/bin is specified in the PATH directive of a config file.
+
+The reason that things work this way is that typically a user will
+install all library files in the Syslinux installation directory, as
+specified with the --directory installer option. This method allows
+the user to omit the PATH directive from their config file and still
+have things work correctly.
+
+
+== AUTHOR ==
+This AsciiDoc derived document is a modified version of the original
+*SYSLINUX* documentation by {author} <{author-email}>. The conversion
+to an AsciiDoc was made by {editor1} <{editor1-email}>
diff --git a/txt/syslinux.cfg.txt b/txt/syslinux.cfg.txt
new file mode 100644
index 00000000..a06fe050
--- /dev/null
+++ b/txt/syslinux.cfg.txt
@@ -0,0 +1,697 @@
+= syslinux.cfg(5) =
+:doctype: manpage
+:revdate: 2012-10-28
+:author: H. Peter Anvin
+:author-email: hpa@zytor.com
+:editor1: Gene Cumm
+:editor1-email: gene.cumm@gmail.com
+:editor1-revlast: 2012-10-28
+:nbsp8: &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
+:nbsp32: {nbsp8}{nbsp8}{nbsp8}{nbsp8}
+:data-uri:
+
+== NAME ==
+syslinux.cfg - *Syslinux* configuration file
+
+
+== DESCRIPTION ==
+Configuration for the boot behavior and user experience of *Syslinux*
+boot loaders, the format of display files and the boot prompt behavior.
+
+Blank lines are ignored.
+
+Note that the configuration file is not completely decoded. Syntax
+different from the one described above may still work correctly in this
+version of *Syslinux*, but may break in a future one.
+
+
+== LOCATION/NAME ==
+*SYSLINUX* (before 4.00) used the configuration filename of
+syslinux.cfg. *EXTLINUX* (merged into *SYSLINUX* as of 4.00) used the
+filename extlinux.conf. Both default to searching for the config file
+in the installed directory (containing ldlinux.sys/extlinux.sys). As of
+4.00, *SYSLINUX* will search for extlinux.conf then syslinux.cfg in each
+directory before falling back to the next directory.
+
+As of 3.35, *SYSLINUX* also searches /boot/syslinux, /syslinux and /.
+
+*ISOLINUX* (before 4.02) used the configuration filename of
+isolinux.cfg, searching /boot/isolinux (starting 2.00), then /isolinux
+and /. As of 4.02, *ISOLINUX* will search for isolinux.cfg then
+syslinux.cfg in /boot/isolinux before searching for the same files in
+/isolinux, /boot/syslinux, /syslinux, and /.
+
+
+== GLOBAL DIRECTIVES - MAIN ==
+*#* comment::
+A line comment. As of version 3.10, the space between the *#* and the
+comment is no longer required.
+
+*MENU* any string::
+(3.00+) A directive for the simple menu system, treated as a comment
+outside the menu. See menu.txt.
+
+*INCLUDE* 'filename'::
+Inserts the contents of another file at this point in the configuration
+file. Files can currently be nested up to 16 levels deep, but it is not
+guaranteed that more than 8 levels will be supported in the future.
+
+*DEFAULT* 'kernel' 'options...'::
+Sets the default command line (which often references a LABEL). If
+*Syslinux* boots automatically, it will act just as if the entries after
+*DEFAULT* had been typed in at the 'boot:' prompt. Multiple uses will
+result in an override.
++
+If no configuration file is present, or no *DEFAULT* or *UI* entry is
+present in the config file, an error message is displayed and the
+'boot:' prompt is shown (3.85+).
+
+*UI* 'module' 'options...'::
+Selects a specific user interface 'module' (typically menu.c32 or
+vesamenu.c32). The command-line interface treats this as a directive
+that overrides the *DEFAULT* directive to load this module instead at
+startup, for an empty command line and at timeout and *PROMPT* directive
+to not prompt (but these directives may have effects on other
+configuration parsers). Multiple uses will result in an override.
+
+*LABEL* 'mylabel'::
+Begin a new *LABEL* clause. If 'mylabel' is entered as the kernel to
+boot, *Syslinux* should instead boot "image" (specified by a directive
+from *KERNEL-LIKE DIRECTIVES*) with any specified *DUAL-PURPOSE
+DIRECTIVES* being used instead of the global instance.
++
+'mylabel' must be unique. Currently the first instance is used but may
+result in an error or undesired behavior. 'mylabel' ends at the first
+character that is not a non-white-space printable character and should
+be restricted to non-white-space typeable characters. Prior to version
+3.32, this would transformed to a DOS compatible format of 8.3 with a
+restricted character set. A *LABEL* clause must contain exactly 1 of
+the *KERNEL-LIKE DIRECTIVES* and may contain 1 each of the *LABEL-ONLY
+DIRECTIVES* or *DUAL-PURPOSE DIRECTIVES*.
++
+Within a *LABEL*, using multiple *KERNEL-LIKE DIRECTIVES* or reuse of
+*LABEL-ONLY DIRECTIVES* or *DUAL-PURPOSE DIRECTIVES* will result in an
+override. Otherwise, multiple instances of the same directive will
+result in the last being effective.
+
+
+== DUAL-PURPOSE DIRECTIVES ==
+Use of any of the *DUAL-PURPOSE DIRECTIVES* as *GLOBAL DIRECTIVES* is
+discouraged if there will be any non-Linux images loaded as *ALL* images
+will get these, including those manually entered at the 'boot:' prompt.
+
+*APPEND* 'options...'::
+Add one or more options to the kernel command line. These are added
+both for automatic and manual boots. The options are added at the very
+beginning of the kernel command line, usually permitting explicitly
+entered kernel options to override them. This is the equivalent of the
+LILO "append" option.
++
+Use of the parameter 'initrd=' supports multiple filenames separated by
+commas (ie 'initrd=initrd_file1,initrd_file2') within a single instance.
+This is mostly useful for initramfs, which can be composed of multiple
+separate cpio or cpio.gz archives.
++
+Note: all initrd files except the last one are zero-padded to a 4K page
+boundary. This should not affect initramfs.
++
+Note: Only the last effective 'initrd=' parameter is used for loading
+initrd files.
+
+*APPEND* -::
+Append nothing. *APPEND* with a single hyphen as argument in a *LABEL*
+section can be used to override a global *APPEND*.
+
+//[FIXME: Shorten subdefinitions]
+*SYSAPPEND* 'bitmask'::
+*IPAPPEND* 'bitmask'::
+(*SYSAPPEND*: 5.10+; *IPAPPEND*: *PXELINUX* only)
+The *SYSAPPEND* option was introduced in *Syslinux* 5.10; it is an
+enhancement of a previous option *IPAPPEND* which was only available on
+*PXELINUX*. 'bitmask' is interpreted as decimal format unless prefixed
+with "0x" for hexadecimal or "0" (zero) for octal. The 'bitmask' is an
+OR (sum) of the following integer options:
+
+ifndef::doctype-manpage[[horizontal]]
+*1*::: An option of the following format should be generated, based on
+the input from the DHCP/BOOTP or PXE boot server and added to the kernel
+command line(see note below; empty for non-PXELINUX variants):
++
+----
+ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
+----
++
+NOTE: The use of option 1 is no substitute for running a DHCP client in
+the booted system and should instead only be used to seed the client for
+a request. Without regular renewals, the lease acquired by the PXE BIOS
+will expire, making the IP address available for reuse by the DHCP
+server.
++
+*2*::: An option of the following format should be generated, in
+dash-separated hexadecimal with leading hardware type (same as for the
+configuration file; see pxelinux.txt.) and added to the kernel command
+line, allowing an initrd program to determine from which interface the
+system booted(empty for non-PXELINUX variants):
++
+----
+BOOTIF=<hardware-address-of-boot-interface>
+----
++
+*4*::: An option of the following format should be generated, in lower
+case hexadecimal in the format normally used for UUIDs (same as for the
+configuration file; see pxelinux.txt.) and added to the kernel command
+line:
++
+----
+SYSUUID=<system uuid>
+----
++
+*8*::: (5.10+) indicate the CPU family and certain particularly
+significant CPU feature bits:
++
+----
+CPU=<family><features>
+----
++
+The <family> is a single digit from 3 (i386) to 6 (i686 or higher.) The
+following CPU features are currently reported; additional flags may be
+added in the future:
++
+....
+P Physical Address Extension (PAE)
+V Intel Virtualization Technology (VT/VMX)
+T Intel Trusted Exection Technology (TXT/SMX)
+X Execution Disable (XD/NX)
+L Long Mode (x86-64)
+S AMD SMX virtualization
+....
++
+*DMI*::: (5.10+) The following strings are derived from DMI/SMBIOS
+information if available:
++
+ Bit String Significance
+ -------------------------------------------------------------
+ 0x00010 SYSVENDOR= System vendor name
+ 0x00020 SYSPRODUCT= System product name
+ 0x00040 SYSVERSION= System version
+ 0x00080 SYSSERIAL= System serial number
+ 0x00100 SYSSKU= System SKU
+ 0x00200 SYSFAMILY= System family
+ 0x00400 MBVENDOR= Motherboard vendor name
+ 0x00800 MBVERSION= Motherboard version
+ 0x01000 MBSERIAL= Motherboard serial number
+ 0x02000 MBASSET= Motherboard asset tag
+ 0x04000 BIOSVENDOR= BIOS vendor name
+ 0x08000 BIOSVERSION= BIOS version
+ 0x10000 SYSFF= System form factor
++
+If these strings contain white-space characters, they are replaced with
+underscores (_).
++
+The system form factor value is a number defined in the SMBIOS
+specification, available at http://www.dmtf.org/. As of version 2.7.1
+of the specification, the following values are defined:
++
+ 1 Other
+ 2 Unknown
+ 3 Desktop
+ 4 Low profile desktop
+ 5 Pizza box
+ 6 Mini tower
+ 7 Tower
+ 8 Portble
+ 9 Laptop
+ 10 Notebook
+ 11 Handheld
+ 12 Docking station
+ 13 All-in-one
+ 14 Subnotebook
+ 15 Space-saving
+ 16 Lunch box
+ 17 Main server chassis
+ 18 Expansion chassis
+ 19 Subchassis
+ 20 Bus expansion chassis
+ 21 Peripheral chassis
+ 22 RAID chassis
+ 23 Rack mount chasss
+ 24 Sealed-case PC
+ 25 Multi-system chassis
+ 26 Compact PCI
+ 27 Advanced TCI
+ 28 Blade
+ 29 Blade enclosure
+
+
+
+== KERNEL-LIKE DIRECTIVES ==
+// Alpha sort after KERNEL and LINUX
+*KERNEL* 'image'::
+Load a kernel-like file 'image' with automatic filetype detection based
+on file extension, listed under the non-auto-detecting directives,
+defaulting to *LINUX*.
+
+//[FIXME: Should "'image' as " be removed entirely or added to all?
+*LINUX* is used as an example]
+*LINUX* 'image'::
+Load 'image' as a Linux-like kernel. MEMDISK is an example of a
+non-Linux kernel loaded in a Linux-like fashion.
+
+*BOOT* 'image'::
+(*ISOLINUX* only: .bin; *SYSLINUX* only: .bs) Load a boot sector. .bin
+is a "CD boot sector" and .bs is a regular disk boot sector.
+
+*BSS* 'image'::
+(*SYSLINUX* only: .bss) Load a BSS image, a .bs image with the DOS
+superblock patched in.
+
+*COMBOOT* 'image'::
+(.com, .cbt; Removed as of 5.00) Load a *Syslinux* COMBOOT image. .com
+images may also be runnable from DOS while .cbt images are not. See
+also *comboot.txt*
+
+*COM32* 'image'::
+(.c32) Load a *Syslinux* COM32 (32-bit *COMBOOT*) image. See also
+*comboot.txt*
+
+*CONFIG* 'image'::
+Load a new configuration file. The configuration file is read, the
+working directory is changed (if specified via an *APPEND*), then the
+configuration file is parsed.
+
+*FDIMAGE* 'image'::
+(Removed as of 4.05, added 1.65; *ISOLINUX* only: .img) Load a disk
+image.
+
+*LOCALBOOT* 'type'::
+(*PXELINUX* 1.53+; *ISOLINUX* ??3.10+; *SYSLINUX* 3.70+)Attempt a
+different local boot method. The special value -1 causes the boot
+loader to report failure to the BIOS, which, on recent BIOSes, should
+mean that the next boot device in the boot sequence should be activated.
+ Values other than those documented may produce undesired results.
++
+On *PXELINUX*, 'type' 0 means perform a normal boot. 'type' 4 will
+perform a local boot with the Universal Network Driver Interface (UNDI)
+driver still resident in memory. Finally, 'type' 5 will perform a local
+boot with the entire PXE stack, including the UNDI driver, still
+resident in memory. All other values are undefined. If you don't know
+what the UNDI or PXE stacks are, don't worry -- you don't want them,
+just specify 0.
++
+On *ISOLINUX*/*SYSLINUX*, the 'type' specifies the local drive number to
+boot from; 0x00 is the primary floppy drive and 0x80 is the primary hard
+drive.
+
+*PXE* 'image'::
+(*PXELINUX* only: .0) Load a PXE NBP (Network Boot Program) image. The
+PXE protocol does not provide any means for specifiying or using a
+command line or initrd.
+
+
+== LABEL-ONLY DIRECTIVES ==
+*INITRD* 'initrd_file'::
+(3.71+) An initrd can be specified in a separate statement (INITRD)
+instead of as part of the *APPEND* statement; this functionally appends
+"initrd=initrd_file" to the kernel command line. Like 'initrd=', this
+also supports multiple comma separated file names (see *APPEND*).
+
+
+== GLOBAL DIRECTIVES - SECONDARY ==
+These are global directives that are of lesser importance, often
+affecting the user experience and not the boot process.
+
+*ALLOWOPTIONS* 'flag_val'::
+If 'flag_val' is 0, the user is not allowed to specify any arguments on
+the kernel command line. The only options recognized are those
+specified in an *APPEND*) statement. The default is 1.
+
+*IMPLICIT* 'flag_val'::
+If 'flag_val' is 0, do not load a kernel image unless it has been
+explicitly named in a *LABEL* statement. The default is 1.
+
+*TIMEOUT* 'timeout'::
+Indicates how long to wait at the 'boot:' prompt until booting
+automatically, in units of 1/10 s. The timeout is cancelled as soon as
+the user types anything on the keyboard, the assumption being that the
+user will complete the command line already begun. The timer is reset
+to 0 upon return from an unsuccessful attempt to boot or from a module.
+A timeout of zero (the default) will disable the timeout completely.
+
+*TOTALTIMEOUT* 'timeout'::
+Indicates how long to wait until booting automatically, in units of
+1/10 s. This timeout is *not* cancelled by user input, and can thus be
+used to deal with serial port glitches or "the user walked away" type
+situations. A timeout of zero (the default) will disable the timeout
+completely.
++
+Both *TIMEOUT* and *TOTALTIMEOUT* can be used together, for example:
++
+----
+# Wait 5 seconds unless the user types something, but
+# always boot after 15 minutes.
+TIMEOUT 50
+TOTALTIMEOUT 9000
+----
+
+// FIXME: be consistent
+*ONTIMEOUT* 'kernel options...'::
+Sets the command line invoked on a timeout (which often references a
+LABEL). If not specified, 'UI' (if used) or 'DEFAULT is used.
+
+*ONERROR* 'kernel options...'::
+If a kernel image is not found (either due to it not existing, or
+because *IMPLICIT* is set), run the specified command. The faulty
+command line is appended to the specified options, so if the *ONERROR*
+directive reads as:
++
+----
+ONERROR xyzzy plugh
+----
++
+and the command line as entered by the user is:
++
+----
+foo bar baz
+----
++
+*Syslinux* will execute the following as if entered by the user:
++
+----
+xyzzy plugh foo bar baz
+----
+
+*SERIAL* 'port [baudrate [flowcontrol]]'::
+Enables a serial port to act as the console. 'port' is a number (0 =
+/dev/ttyS0 = COM1, etc.) or an I/O port address (e.g. 0x3F8); if
+'baudrate' is omitted, the baud rate defaults to 9600 bps. The serial
+parameters are hardcoded to be 8 bits, no parity, 1 stop bit.
++
+'flowcontrol' is a combination of the following bits:
++
+....
+0x001 - Assert DTR
+0x002 - Assert RTS
+0x008 - Enable interrupts
+0x010 - Wait for CTS assertion
+0x020 - Wait for DSR assertion
+0x040 - Wait for RI assertion
+0x080 - Wait for DCD assertion
+0x100 - Ignore input unless CTS asserted
+0x200 - Ignore input unless DSR asserted
+0x400 - Ignore input unless RI asserted
+0x800 - Ignore input unless DCD asserted
+....
++
+All other bits are reserved.
++
+Typical values are:
++
+....
+ 0 - No flow control (default)
+0x303 - Null modem cable detect
+0x013 - RTS/CTS flow control
+0x813 - RTS/CTS flow control, modem input
+0x023 - DTR/DSR flow control
+0x083 - DTR/DCD flow control
+....
++
+For the *SERIAL* directive to be guaranteed to work properly, it should
+be the first directive in the configuration file.
++
+NOTE: 'port' values from 0 to 3 means the first four serial ports
+detected by the BIOS. They may or may not correspond to the legacy port
+values 0x3F8, 0x2F8, 0x3E8, 0x2E8.
++
+Enabling interrupts (setting the 0x008 bit) may give better
+responsiveness without setting the *NOHALT* option, but could
+potentially cause problems with buggy BIOSes.
++
+This option is "sticky" and is not automatically reset when loading a
+new configuration file with the CONFIG command.
+
+*NOHALT* 'flag_val'::
+If 'flag_val' is 1, don't halt the processor while idle. Halting the
+processor while idle significantly reduces the power consumption, but
+can cause poor responsiveness to the serial console, especially when
+using scripts to drive the serial console, as opposed to human
+interaction.
+
+*CONSOLE* 'flag_val'::
+If 'flag_val' is 0, disable output to the normal video console. If
+'flag_val' is 1, enable output to the video console (this is the
+default.)
++
+Some BIOSes try to forward this to the serial console and sometimes make
+a total mess thereof, so this option lets you disable the video console
+on these systems.
+
+*FONT* 'filename'::
+Load a font in .psf format before displaying any output (except the
+copyright line, which is output as ldlinux.sys itself is loaded.)
+*Syslinux* only loads the font onto the video card; if the .psf file
+contains a Unicode table it is ignored. This only works on EGA and VGA
+cards; hopefully it should do nothing on others.
+
+*KBDMAP* 'keymap'::
+Install a simple keyboard map. The keyboard remapper used is *very*
+simplistic (it simply remaps the keycodes received from the BIOS, which
+means that only the key combinations relevant in the default layout --
+usually U.S. English -- can be mapped) but should at least help people
+with AZERTY keyboard layout and the locations of = and , (two special
+characters used heavily on the Linux kernel command line.)
++
+The included program keytab-lilo.pl from the LILO distribution can be
+used to create such keymaps. The file keytab-lilo.txt contains the
+documentation for this program.
+
+*DISPLAY* 'filename'::
+Displays the indicated file on the screen at boot time (before the boot:
+prompt, if displayed). Please see the section below on *DISPLAY* files.
++
+NOTE: If the file is missing, this option is simply ignored.
+
+*SAY* 'message'::
+Prints the message on the screen.
+
+*PROMPT* 'flag_val'::
+If 'flag_val' is 0, display the boot: prompt only if the Shift or Alt
+key is pressed, or Caps Lock or Scroll lock is set (this is the
+default). If 'flag_val' is 1, always display the boot: prompt.
+
+*NOESCAPE* 'flag_val'::
+If 'flag_val' is set to 1, ignore the Shift/Alt/Caps Lock/Scroll Lock
+escapes. Use this (together with PROMPT 0) to force the default boot
+alternative.
+
+*NOCOMPLETE* 'flag_val'::
+If 'flag_val' is set to 1, the Tab key does not display labels at the
+boot: prompt.
+
+// ...etc...
+*F1* 'filename'::
+*F2* 'filename'::
+*F3* 'filename'::
+*F4* 'filename'::
+*F5* 'filename'::
+*F6* 'filename'::
+*F7* 'filename'::
+*F8* 'filename'::
+*F9* 'filename'::
+*F10* 'filename'::
+*F11* 'filename'::
+*F12* 'filename'::
+Displays the indicated file on the screen when a function key is pressed
+at the boot: prompt. This can be used to implement pre-boot online help
+(presumably for the kernel command line options.) Please see the
+section below on DISPLAY files.
++
+When using the serial console, press <Ctrl-F><digit> to get to the help
+screens, e.g. <Ctrl-F><2> to get to the F2 screen. For F10-F12, hit
+<Ctrl-F><A>, <Ctrl-F>B, <Ctrl-F>C. For compatibility with earlier
+versions, F10 can also be entered as <Ctrl-F>0.
+
+*PATH* 'path'::
+(5.00+) Specify a space-separated (' '; 5.00-5.10 was a colon ':') list
+of directories to search when attempting to load modules. This directive
+is useful for specifying the directories containing the lib*.c32 library
+files as other modules may be dependent on these files, but may not
+reside in the same directory. Multiple instances will append additional
+paths.
+
+*SENDCOOKIES* 'bitmask'::
+(*PXELINUX* 5.10+) When downloading files over http, the SYSAPPEND
+strings are prepended with _Syslinux_ and sent to the server as cookies.
+The cookies are URL-encoded; whitespace is *not* replaced with
+underscores.
++
+This command limits the cookies send; 0 means no cookies. The default
+is -1, meaning send all cookies.
++
+This option is "sticky" and is not automatically reset when loading a
+new configuration file with the CONFIG command.
+
+
+== DISPLAY FILE FORMAT ==
+DISPLAY and function-key help files are text files in either DOS or UNIX
+format (with or without <CR>). In addition, the following special codes
+are interpreted:
+
+//[FIXME]: #1 doesn't break; #2 as-is; #3 broken but not on right; #4
+identical to #3
+// horizontal extends the line's label, reducing the definition
+// tab or space to shift explanation ? align beginning or end?
+
+// ifndef::doctype-manpage[[horizontal]]
+*<FF>*:: {nbsp32} = <Ctrl-L> = ASCII 12 +
+Clear the screen, home the cursor. Note that the screen is filled with
+the current display color.
+
+*<FF>*::
+= <Ctrl-L> = ASCII 12; Clear the screen, home the cursor. Note that the
+screen is filled with the current display color.
+
+*<FF>*:: <FF> = <Ctrl-L> = ASCII 12
++
+Clear the screen, home the cursor. Note that the screen is filled with
+the current display color.
+
+*<FF>*::
+<FF> = <Ctrl-L> = ASCII 12 +
+Clear the screen, home the cursor. Note that the screen is filled with
+the current display color.
+
+*<SI>*'<bg><fg>':: <SI> = <Ctrl-O> = ASCII 15
++
+Set the display colors to the specified background and foreground
+colors, where <bg> and <fg> are the 2 hex digits representing 1 byte,
+corresponding to the standard PC display attributes:
++
+ 0 = black 8 = dark grey
+ 1 = dark blue 9 = bright blue
+ 2 = dark green a = bright green
+ 3 = dark cyan b = bright cyan
+ 4 = dark red c = bright red
+ 5 = dark purple d = bright purple
+ 6 = brown e = yellow
+ 7 = light grey f = white
++
+Picking a bright color (8-f) for the background results in the
+corresponding dark color (0-7), with the foreground flashing.
++
+Colors are not visible over the serial console.
+
+*<CAN>*'filename<newline>':: <CAN> = <Ctrl-X> = ASCII 24
++
+If a VGA display is present, enter graphics mode and display the graphic
+included in the specified file. The file format is an ad hoc format
+called LSS16; the included Perl program "ppmtolss16" can be used to
+produce these images. This Perl program also includes the file format
+specification.
++
+The image is displayed in 640x480 16-color mode. Once in graphics mode,
+the display attributes (set by <SI> code sequences) work slightly
+differently: the background color is ignored, and the foreground colors
+are the 16 colors specified in the image file. For that reason,
+ppmtolss16 allows you to specify that certain colors should be assigned
+to specific color indicies.
++
+Color indicies 0 and 7, in particular, should be chosen with care: 0 is
+the background color, and 7 is the color used for the text printed by
+*Syslinux* itself.
+
+*<EM>*:: <EM> = <Ctrl-Y> = ASCII 25 +
+If we are currently in graphics mode, return to text mode.
+
+*<DLE>*..*<ETB>*:: <Ctrl-P>..<Ctrl-W> = ASCII 16-23
++
+These codes can be used to select which modes to print a certain part of
+the message file in. Each of these control characters select a specific
+set of modes (text screen, graphics screen, serial port) for which the
+output is actually displayed:
++
+ Character Text Graph Serial
+ ------------------------------------------------------
+ <DLE> = <Ctrl-P> = ASCII 16 No No No
+ <DC1> = <Ctrl-Q> = ASCII 17 Yes No No
+ <DC2> = <Ctrl-R> = ASCII 18 No Yes No
+ <DC3> = <Ctrl-S> = ASCII 19 Yes Yes No
+ <DC4> = <Ctrl-T> = ASCII 20 No No Yes
+ <NAK> = <Ctrl-U> = ASCII 21 Yes No Yes
+ <SYN> = <Ctrl-V> = ASCII 22 No Yes Yes
+ <ETB> = <Ctrl-W> = ASCII 23 Yes Yes Yes
++
+For example, the following will actually print out which mode the
+console is in:
++
+ <DC1>Text mode<DC2>Graphics mode<DC4>Serial port<ETB>
+
+*<SUB>*:: <SUB> = <Ctrl-Z> = ASCII 26
++
+End of file (DOS convention).
+
+*<BEL>*:: <BEL> = <Ctrl-G> = ASCII 7 +
+Beep the speaker.
+
+
+== BOOT LOADER IDS USED ==
+The Linux boot protocol supports a "boot loader ID", a single byte where
+the upper nybble specifies a boot loader family (3 = *Syslinux*) and the
+lower nybble is version or, in the case of *Syslinux*, media:
+
+ 0x31 (49) = SYSLINUX
+ 0x32 (50) = PXELINUX
+ 0x33 (51) = ISOLINUX
+ 0x34 (52) = EXTLINUX
+
+In recent versions of Linux, this ID is available as
+/proc/sys/kernel/bootloader_type.
+
+
+== NOVICE PROTECTION ==
+*Syslinux* will attempt to detect booting on a machine with too little
+memory, which means the Linux boot sequence cannot complete. If so, a
+message is displayed and the boot sequence aborted. Holding down the
+Ctrl key while booting disables this feature.
+
+Any file that *Syslinux* uses can be marked hidden, system or readonly
+if so is convenient; *Syslinux* ignores all file attributes. The
+*SYSLINUX* installer automatically sets the readonly/hidden/system
+attributes on LDLINUX.SYS.
+
+== EXAMPLE ==
+Here are some sample config files:
+----
+# SERIAL 0 115200
+DEFAULT linux
+PROMPT 1
+TIMEOUT 600
+
+LABEL linux
+ LINUX vmlinuz
+ APPEND initrd=initrd1.gz,initrd2.gz
+
+LABEL m
+ COM32 menu.c32
+----
+In this example, serial port use is disabled but can be enabled by
+uncommenting the first line and utilize serial port 0 at 115200 bps. If
+'linux' is typed on the command line, the kernel-like file 'vmlinuz' is
+executed as a Linux kernel, initrd files initrd1.gz and initrd2.gz are
+loaded as initial ramdisk files (like cpio.gz files for initramfs). If
+'m' is typed on the command line, the COM32 module 'menu.c32' is
+executed to launch a menu system.
+
+
+
+== KNOWN BUGS ==
+include::com-bug.txt[]
+
+
+== BUG REPORTS ==
+include::com-rpt.txt[]
+
+
+== AUTHOR ==
+This AsciiDoc derived document is a modified version of the original
+*SYSLINUX* documentation by {author} <{author-email}>. The conversion
+to an AsciiDoc was made by {editor1} <{editor1-email}>
diff --git a/txt/syslinux.txt b/txt/syslinux.txt
new file mode 100644
index 00000000..59666635
--- /dev/null
+++ b/txt/syslinux.txt
@@ -0,0 +1,219 @@
+= syslinux(1) =
+:doctype: manpage
+:revdate: 2013-06-12
+:author: H. Peter Anvin
+:author-email: hpa@zytor.com
+:editor1: Gene Cumm
+:editor1-email: gene.cumm@gmail.com
+:editor1-revlast: 2013-06-12
+
+
+== NAME ==
+syslinux - Install SYSLINUX to a file system
+
+
+== SYNOPSIS ==
+[verse]
+*syslinux* ['OPTIONS'] 'DEVICE'
+*extlinux* ['OPTIONS'] 'PATH'
+*syslinux* [-h | --help | -v | --version]
+*extlinux* [-h | --help | -v | --version]
+
+
+== DESCRIPTION ==
+Install *SYSLINUX* to the 'DEVICE'/'PATH', altering the boot sector and
+installing the 'ldlinux.sys' boot loader file. For the Linux installer
+extlinux, 'PATH' is the desired path for the control files on a mounted,
+supported file system and sets the install-time working directory. For
+all others, 'DEVICE' must specify a FAT12/FAT16/FAT32 file system. For
+the Linux installers syslinux and syslinux-mtools, 'DEVICE' should be an
+unmounted file system. For the DOS/Win32/Win64 installers, 'DEVICE'
+should be a drive like 'a:' (case insensitive).
+
+For versions ~4.00 and later, either -i/--install or -U/--update must be
+specified unless modifying the ADV of an existing install (options
+tagged with 'ADV') or requesting the help/usage or version info, .
+
+If, during boot, the Shift or Alt keys are held down, or the Caps or
+Scroll locks are set, *Syslinux* will display a *lilo*(8) -style "boot:"
+prompt. The user can then type a kernel file name followed by any kernel
+parameters. The *Syslinux* bootloader does not need to know about the
+kernel or config files in advance.
+
+*Syslinux* supports the loading of initial ramdisks (initrd) and the
+bzImage kernel format.
+
+Please note, the ldlinux.sys boot loader file is flagged as immutable
+(where applicable) and is modified after copying in to help ensure
+boot-time integrity. File systems with a sufficiently large boot loader
+reserved area, like btrfs, will have ldlinux.sys installed there rather
+than as a normal file. Prior to version 4.00, extlinux would install a
+file extlinux.sys which versions 4.00 and later installers will replace with ldlinux.sys.
+
+
+== OPTIONS ==
+// "See"/"See also" notes should reference long options.
+=== Standalone options ===
+*-i*, *--install*::
+(~4.00+) Install SYSLINUX, regardless of an existing install.
+
+*-U*, *--update*::
+(~4.00+) Update an existing SYSLINUX/EXTLINUX install. If no Syslinux
+boot loader is present, return an error.
+
+*-h*, *--help*::
+Display help/usage information.
+
+*-v*, *--version*::
+Display version information and exit immediately.
+
+=== Regular Options ===
+// Sorted generally by short argument
+*-a*, *--active*::
+(DOS/Win32/Win64 ONLY) Mark the install target file system's partition
+active.
+
+*-d*, *--directory* 'subdirectory'::
+(Not necessary for extlinux as it is implied by 'PATH') Install the
+*SYSLINUX* control files in a subdirectory with the specified name
+(relative to the root directory on the device).
+
+*--device* 'DEVICE'::
+(extlinux ONLY; 4.06+) Force use of a specific block device (experts
+only).
+
+*-f*, *--force*::
+Force install even if it appears unsafe. Before 4.00, -f was used for
+--offset in the Linux installers.
+
+*-H*, *--heads* 'head-count'::
+Override the detected number of heads for the geometry. See also
+*--sector*.
+
+*-m*, *--mbr*:
+(DOS/Win32/Win64 ONLY) Install the regular Syslinux MBR code to the MBR.
+
+*-M*, *--menu-save*::
+(4.00+; ADV) Set the label to select as default on the next boot.
+
+*-o*, *--once* 'command'::
+(ADV) Declare a boot command to be tried on the first boot only. The
+use of *-o* for the Linux installers syslinux or syslinux-mtools has
+been deprecated as of \~4.00 and is no longer valid as of ~4.02.
+
+*-O*, *--clear-once*::
+Clear the boot-once command. See also *--once*.
+
+*-r*, *--raid*::
+(ADV) RAID mode. If boot fails, tell the BIOS to boot the next device
+in the boot sequence (usually the next hard disk) instead of stopping
+with an error message. This is useful for RAID-1 booting.
+
+*--reset-adv*::
+(ADV) Reset auxilliary data vector.
+
+*-S*, *--sectors* 'sector-count'::
+Override the detected number of sectors for the geometry. See also
+*--head*.
+
+*-s*, *--stupid*::
+Install a "safe, slow and stupid" version of *SYSLINUX*. This version
+may work on some very buggy BIOSes on which *SYSLINUX* would otherwise
+fail. If you find a machine on which the -s option is required to make
+it boot reliably, please send as much info about your machine as you
+can, and include the failure mode.
+
+*-t*, *--offset* 'offset'::
+(Linux syslinux/syslinux-mtools ONLY) Indicates that the filesystem is
+at an offset from the base of the device or file.
+
+*-z*, *--zipdrive*
+Assume zipdrive geometry ('--heads 64 --sectors 32'). See also *--head*
+and *--sector*.
+
+
+== EXAMPLES ==
+=== Booting DOS ===
+For booting DOS and other similar operating systems, there is an easy
+and generally reliable solution to substitute in SYSLINUX as the primary
+boot loader.
+
+- Make a DOS-bootable disk; The following are possible commands:
+
+ format a: /s
+ sys a:
+
+- Copy the DOS boot sector off using Linux or copybs.com:
+
+ dd if=/dev/fd0 of=dos.bss bs=512 count=1
+ copybs a: a:dos.bss
+
+- Install SYSLINUX using one of:
+
+ syslinux a:
+ syslinux /dev/fd0 (before 4.00)
+ syslinux -i /dev/fd0 (4.00+)
+
+- For Linux, mount the disk and copy the dos.bss to the disk:
+
+ mount -t msdos /dev/fd0 /mnt
+ cp dos.bss /mnt
+
+- Copy a Linux kernel image and initrd payload files:
+*Linux:*::
+ cp vmlinux /mnt
+ cp initrd.gz /mnt
+*DOS/Windows:*::
+ copy vmlinux a:
+ copy initrd.gz a:
+
+- For Linux, umount the disk (if applicable):
+
+ umount /mnt
+
+=== MBR ===
+In order to boot from a hard disk (or hard disk-like device) in BIOS
+mode, an appropriate MBR boot block must also be installed in the MBR
+(first sector or 512 bytes of the disk), occupying at most 440 bytes.
+
+*DOS/Windows:*::
+If using FDISK, FDISK or a similar application must also be used to mark
+the partition as active.
++
+ fdisk /mbr
+ OR
+ syslinux -ma c:
+
+*Linux:*::
++
+ dd bs=440 count=1 conv=notrunc if=mbr/mbr.bin of=/dev/sda
++
+For altmbr.bin, an easy way to overwrite the MBR boot block and specify
+the partion number is:
++
+ printf '\1' | cat altmbr.bin - | dd bs=440 count=1 \
+ iflag=fullblock conv=notrunc of=/dev/sda
++
+Note: using 'cat' for writing the MBR can under some circumstances cause
+data loss or overwritting. For this reason, using 'dd' is recommended
+for all situations.
+
+//[FIXME]: any clean way to handle the above long command for manpage?
+
+
+== SEE ALSO ==
+*syslinux.cfg*(5), *syslinux-cli*(1), *lilo*(8), *keytab-lilo.pl*(8),
+*fdisk*(8), *mkfs*(8), *superformat*(1).
+
+
+== AUTHOR ==
+This AsciiDoc derived document is a modified version of the original
+*SYSLINUX* documentation by {author} <{author-email}>. The conversion to
+a manpage was made by Arthur Korn <arthur@korn.ch>. The conversion to
+an AsciiDoc was made by {editor1} <{editor1-email}>
+
+
+== COPYRIGHT ==
+Copyright \(C) 1994-2012 {author}. Free use of this software is granted
+under the terms of the GNU General Public License (GPL), version 2
+(GPLv2) (or, at your option, any later version).
diff --git a/version b/version
index 94249a5e..080a8c87 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-4.07 2013
+5.11 2013
diff --git a/win/syslinux.c b/win/syslinux.c
index 669450eb..c291f005 100644
--- a/win/syslinux.c
+++ b/win/syslinux.c
@@ -236,6 +236,57 @@ int libfat_readfile(intptr_t pp, void *buf, size_t secsize,
return secsize;
}
+static void move_file(char *pathname, char *filename)
+{
+ char new_name[strlen(opt.directory) + 16];
+ char *cp = new_name + 3;
+ const char *sd;
+ int slash = 1;
+
+ new_name[0] = opt.device[0];
+ new_name[1] = ':';
+ new_name[2] = '\\';
+
+ for (sd = opt.directory; *sd; sd++) {
+ char c = *sd;
+
+ if (c == '/' || c == '\\') {
+ if (slash)
+ continue;
+ c = '\\';
+ slash = 1;
+ } else {
+ slash = 0;
+ }
+
+ *cp++ = c;
+ }
+
+ /* Skip if subdirectory == root */
+ if (cp > new_name + 3) {
+ if (!slash)
+ *cp++ = '\\';
+
+ memcpy(cp, filename, 12);
+
+ /* Delete any previous file */
+ SetFileAttributes(new_name, FILE_ATTRIBUTE_NORMAL);
+ DeleteFile(new_name);
+ if (!MoveFile(pathname, new_name)) {
+ fprintf(stderr,
+ "Failed to move %s to destination directory: %s\n",
+ filename, opt.directory);
+
+ SetFileAttributes(pathname, FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ } else
+ SetFileAttributes(new_name, FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ }
+}
+
int main(int argc, char *argv[])
{
HANDLE f_handle, d_handle;
@@ -249,6 +300,7 @@ int main(int argc, char *argv[])
static char drive_name[] = "\\\\.\\?:";
static char drive_root[] = "?:\\";
static char ldlinux_name[] = "?:\\ldlinux.sys";
+ static char ldlinuxc32_name[] = "?:\\ldlinux.c32";
const char *errmsg;
struct libfat_filesystem *fs;
libfat_sector_t s, *secp;
@@ -290,6 +342,7 @@ int main(int argc, char *argv[])
/* Determines the drive type */
drive_name[4] = opt.device[0];
ldlinux_name[0] = opt.device[0];
+ ldlinuxc32_name[0] = opt.device[0];
drive_root[0] = opt.device[0];
drive_type = GetDriveType(drive_root);
@@ -340,10 +393,12 @@ int main(int argc, char *argv[])
/* Change to normal attributes to enable deletion */
/* Just ignore error if the file do not exists */
SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_NORMAL);
+ SetFileAttributes(ldlinuxc32_name, FILE_ATTRIBUTE_NORMAL);
/* Delete the file */
/* Just ignore error if the file do not exists */
DeleteFile(ldlinux_name);
+ DeleteFile(ldlinuxc32_name);
/* Initialize the ADV -- this should be smarter */
syslinux_reset_adv(syslinux_adv);
@@ -463,52 +518,40 @@ map_done:
CloseHandle(f_handle);
/* Move the file to the desired location */
- if (opt.directory) {
- char new_ldlinux_name[strlen(opt.directory) + 16];
- char *cp = new_ldlinux_name + 3;
- const char *sd;
- int slash = 1;
-
- new_ldlinux_name[0] = opt.device[0];
- new_ldlinux_name[1] = ':';
- new_ldlinux_name[2] = '\\';
-
- for (sd = opt.directory; *sd; sd++) {
- char c = *sd;
-
- if (c == '/' || c == '\\') {
- if (slash)
- continue;
- c = '\\';
- slash = 1;
- } else {
- slash = 0;
- }
+ if (opt.directory)
+ move_file(ldlinux_name, "ldlinux.sys");
- *cp++ = c;
- }
+ f_handle = CreateFile(ldlinuxc32_name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN, NULL);
- /* Skip if subdirectory == root */
- if (cp > new_ldlinux_name + 3) {
- if (!slash)
- *cp++ = '\\';
-
- memcpy(cp, "ldlinux.sys", 12);
-
- /* Delete any previous file */
- SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL);
- DeleteFile(new_ldlinux_name);
- if (!MoveFile(ldlinux_name, new_ldlinux_name))
- SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY |
- FILE_ATTRIBUTE_SYSTEM |
- FILE_ATTRIBUTE_HIDDEN);
- else
- SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY |
- FILE_ATTRIBUTE_SYSTEM |
- FILE_ATTRIBUTE_HIDDEN);
- }
+ if (f_handle == INVALID_HANDLE_VALUE) {
+ error("Unable to create ldlinux.c32");
+ exit(1);
}
+ /* Write ldlinux.c32 file */
+ if (!WriteFile(f_handle, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len,
+ &bytes_written, NULL) ||
+ bytes_written != syslinux_ldlinuxc32_len) {
+ error("Could not write ldlinux.c32");
+ exit(1);
+ }
+
+ /* Now flush the media */
+ if (!FlushFileBuffers(f_handle)) {
+ error("FlushFileBuffers failed");
+ exit(1);
+ }
+
+ CloseHandle(f_handle);
+
+ /* Move the file to the desired location */
+ if (opt.directory)
+ move_file(ldlinuxc32_name, "ldlinux.c32");
+
/* Make the syslinux boot sector */
syslinux_make_bootsect(sectbuf, fs_type);
diff --git a/win32/Makefile b/win32/Makefile
index f960998a..9ff8a453 100644
--- a/win32/Makefile
+++ b/win32/Makefile
@@ -56,6 +56,7 @@ LIBSRC = ../libinstaller/fs.c \
../libinstaller/getopt/getopt_long.c \
../libinstaller/bootsect_bin.c \
../libinstaller/ldlinux_bin.c \
+ ../libinstaller/ldlinuxc32_bin.c \
../libinstaller/mbr_bin.c \
$(wildcard ../libfat/*.c)
LIBOBJS = $(patsubst %.c,%.obj,$(notdir $(LIBSRC)))
diff --git a/win64/Makefile b/win64/Makefile
index fe60793c..50132d48 100644
--- a/win64/Makefile
+++ b/win64/Makefile
@@ -46,6 +46,7 @@ LIBSRC = ../libinstaller/fs.c \
../libinstaller/getopt/getopt_long.c \
../libinstaller/bootsect_bin.c \
../libinstaller/ldlinux_bin.c \
+ ../libinstaller/ldlinuxc32_bin.c \
../libinstaller/mbr_bin.c \
$(wildcard ../libfat/*.c)
LIBOBJS = $(patsubst %.c,%.obj,$(notdir $(LIBSRC)))