From ec75c85f9ef53f03840c2c128e4348532e815c37 Mon Sep 17 00:00:00 2001 From: Espressif Systems Date: Tue, 31 Mar 2015 14:23:10 +0800 Subject: [PATCH] update version to 0.9.9, have a try. --- Makefile | 177 +- app/Makefile | 19 - app/gen_misc.bat | 130 +- app/gen_misc.sh | 133 +- app/gen_misc_plus.bat | 27 - app/gen_misc_plus.sh | 24 - app/readme.txt | 40 + app/user/user_main.c | 5 +- bin/boot_v1.3(b3).bin | Bin 0 -> 2544 bytes bin/esp_init_data_default.bin | Bin 0 -> 128 bytes include/espressif/c_types.h | 1 + include/espressif/esp_common.h | 4 +- include/espressif/esp_libc.h | 12 + include/espressif/esp_softap.h | 11 +- include/espressif/esp_sta.h | 2 +- include/espressif/esp_system.h | 14 +- include/espressif/esp_wifi.h | 9 + include/espressif/spi_flash.h | 3 + include/espressif/version.h | 12 - include/freertos/FreeRTOSConfig.h | 4 +- include/freertos/portmacro.h | 52 +- include/lwip/lwip/api.h | 5 + include/lwip/lwipopts.h | 32 +- include/udhcp/common.h | 2 +- ....app1.ld => eagle.app.v6.new.1024.app1.ld} | 44 +- ld/eagle.app.v6.new.1024.app2.ld | 217 + ld/eagle.app.v6.new.512.app1.ld | 217 + ld/eagle.app.v6.new.512.app2.ld | 217 + ld/eagle.app.v6.old.1024.app1.ld | 217 + ld/eagle.app.v6.old.1024.app2.ld | 217 + ld/eagle.app.v6.old.512.app1.ld | 217 + ...6.app2.ld => eagle.app.v6.old.512.app2.ld} | 42 +- lib/libfreertos.a | Bin 65624 -> 65828 bytes lib/libjson.a | Bin 31274 -> 31274 bytes lib/liblwip.a | Bin 275924 -> 270468 bytes lib/libmain.a | Bin 0 -> 123992 bytes lib/libnet80211.a | Bin 0 -> 183796 bytes lib/libphy.a | Bin 0 -> 184684 bytes lib/libpp.a | Bin 0 -> 186122 bytes lib/libssl.a | Bin 137680 -> 137692 bytes lib/libudhcp.a | Bin 43226 -> 42930 bytes lib/libwpa.a | Bin 0 -> 160156 bytes third_party/freertos/croutine.c | 12 +- third_party/freertos/heap_4.c | 12 +- third_party/freertos/list.c | 2 +- third_party/freertos/port.c | 55 +- third_party/freertos/queue.c | 46 +- third_party/freertos/tasks.c | 18 +- third_party/lwip/FILES | 13 - third_party/lwip/Makefile | 50 - third_party/lwip/api/Makefile | 46 - third_party/lwip/api/api_lib.c | 788 ---- third_party/lwip/api/api_msg.c | 1611 ------- third_party/lwip/api/err.c | 75 - third_party/lwip/api/netbuf.c | 245 - third_party/lwip/api/netdb.c | 353 -- third_party/lwip/api/netifapi.c | 160 - third_party/lwip/api/sockets.c | 2568 ---------- third_party/lwip/api/tcpip.c | 507 -- third_party/lwip/arch/Makefile | 46 - third_party/lwip/arch/sys_arch.c | 414 -- third_party/lwip/core/Makefile | 46 - third_party/lwip/core/def.c | 108 - third_party/lwip/core/dhcp.c | 1779 ------- third_party/lwip/core/dns.c | 988 ---- third_party/lwip/core/inet_chksum.c | 545 --- third_party/lwip/core/init.c | 345 -- third_party/lwip/core/ipv4/Makefile | 46 - third_party/lwip/core/ipv4/autoip.c | 528 --- third_party/lwip/core/ipv4/icmp.c | 338 -- third_party/lwip/core/ipv4/igmp.c | 805 ---- third_party/lwip/core/ipv4/ip4.c | 922 ---- third_party/lwip/core/ipv4/ip4_addr.c | 313 -- third_party/lwip/core/ipv4/ip_frag.c | 863 ---- third_party/lwip/core/ipv6/README | 1 - third_party/lwip/core/ipv6/dhcp6.c | 50 - third_party/lwip/core/ipv6/ethip6.c | 193 - third_party/lwip/core/ipv6/icmp6.c | 338 -- third_party/lwip/core/ipv6/inet6.c | 51 - third_party/lwip/core/ipv6/ip6.c | 1028 ---- third_party/lwip/core/ipv6/ip6_addr.c | 251 - third_party/lwip/core/ipv6/ip6_frag.c | 698 --- third_party/lwip/core/ipv6/mld6.c | 581 --- third_party/lwip/core/ipv6/nd6.c | 1786 ------- third_party/lwip/core/mem.c | 660 --- third_party/lwip/core/memp.c | 485 -- third_party/lwip/core/netif.c | 902 ---- third_party/lwip/core/pbuf.c | 1235 ----- third_party/lwip/core/raw.c | 422 -- third_party/lwip/core/snmp/asn1_dec.c | 657 --- third_party/lwip/core/snmp/asn1_enc.c | 611 --- third_party/lwip/core/snmp/mib2.c | 4146 ----------------- third_party/lwip/core/snmp/mib_structs.c | 1174 ----- third_party/lwip/core/snmp/msg_in.c | 1453 ------ third_party/lwip/core/snmp/msg_out.c | 678 --- third_party/lwip/core/stats.c | 182 - third_party/lwip/core/sys.c | 68 - third_party/lwip/core/tcp.c | 1820 -------- third_party/lwip/core/tcp_in.c | 1652 ------- third_party/lwip/core/tcp_out.c | 1499 ------ third_party/lwip/core/timers.c | 547 --- third_party/lwip/core/udp.c | 1151 ----- third_party/lwip/netif/FILES | 29 - third_party/lwip/netif/Makefile | 46 - third_party/lwip/netif/etharp.c | 1414 ------ third_party/lwip/netif/ethernetif.c | 220 - third_party/lwip/netif/ppp/auth.c | 1334 ------ third_party/lwip/netif/ppp/auth.h | 111 - third_party/lwip/netif/ppp/chap.c | 908 ---- third_party/lwip/netif/ppp/chap.h | 150 - third_party/lwip/netif/ppp/chpms.c | 396 -- third_party/lwip/netif/ppp/chpms.h | 64 - third_party/lwip/netif/ppp/fsm.c | 890 ---- third_party/lwip/netif/ppp/fsm.h | 157 - third_party/lwip/netif/ppp/ipcp.c | 1411 ------ third_party/lwip/netif/ppp/ipcp.h | 106 - third_party/lwip/netif/ppp/lcp.c | 2066 -------- third_party/lwip/netif/ppp/lcp.h | 151 - third_party/lwip/netif/ppp/magic.c | 80 - third_party/lwip/netif/ppp/magic.h | 63 - third_party/lwip/netif/ppp/md5.c | 320 -- third_party/lwip/netif/ppp/md5.h | 55 - third_party/lwip/netif/ppp/pap.c | 628 --- third_party/lwip/netif/ppp/pap.h | 118 - third_party/lwip/netif/ppp/ppp.c | 2052 -------- third_party/lwip/netif/ppp/ppp.h | 201 - third_party/lwip/netif/ppp/ppp_impl.h | 363 -- third_party/lwip/netif/ppp/ppp_oe.c | 1132 ----- third_party/lwip/netif/ppp/pppdebug.h | 73 - third_party/lwip/netif/ppp/randm.c | 249 - third_party/lwip/netif/ppp/randm.h | 81 - third_party/lwip/netif/ppp/readme.txt | 21 - third_party/lwip/netif/ppp/vj.c | 652 --- third_party/lwip/netif/ppp/vj.h | 156 - third_party/lwip/netif/slipif.c | 546 --- tools/gen_appbin.py | 296 +- tools/gen_flashbin.py | 33 - 137 files changed, 2231 insertions(+), 55413 deletions(-) delete mode 100644 app/gen_misc_plus.bat delete mode 100644 app/gen_misc_plus.sh create mode 100644 app/readme.txt create mode 100644 bin/boot_v1.3(b3).bin create mode 100644 bin/esp_init_data_default.bin delete mode 100644 include/espressif/version.h rename ld/{eagle.app.v6.app1.ld => eagle.app.v6.new.1024.app1.ld} (75%) create mode 100644 ld/eagle.app.v6.new.1024.app2.ld create mode 100644 ld/eagle.app.v6.new.512.app1.ld create mode 100644 ld/eagle.app.v6.new.512.app2.ld create mode 100644 ld/eagle.app.v6.old.1024.app1.ld create mode 100644 ld/eagle.app.v6.old.1024.app2.ld create mode 100644 ld/eagle.app.v6.old.512.app1.ld rename ld/{eagle.app.v6.app2.ld => eagle.app.v6.old.512.app2.ld} (77%) create mode 100644 lib/libmain.a create mode 100644 lib/libnet80211.a create mode 100644 lib/libphy.a create mode 100644 lib/libpp.a create mode 100644 lib/libwpa.a delete mode 100644 third_party/lwip/FILES delete mode 100644 third_party/lwip/Makefile delete mode 100644 third_party/lwip/api/Makefile delete mode 100644 third_party/lwip/api/api_lib.c delete mode 100644 third_party/lwip/api/api_msg.c delete mode 100644 third_party/lwip/api/err.c delete mode 100644 third_party/lwip/api/netbuf.c delete mode 100644 third_party/lwip/api/netdb.c delete mode 100644 third_party/lwip/api/netifapi.c delete mode 100644 third_party/lwip/api/sockets.c delete mode 100644 third_party/lwip/api/tcpip.c delete mode 100644 third_party/lwip/arch/Makefile delete mode 100644 third_party/lwip/arch/sys_arch.c delete mode 100644 third_party/lwip/core/Makefile delete mode 100644 third_party/lwip/core/def.c delete mode 100644 third_party/lwip/core/dhcp.c delete mode 100644 third_party/lwip/core/dns.c delete mode 100644 third_party/lwip/core/inet_chksum.c delete mode 100644 third_party/lwip/core/init.c delete mode 100644 third_party/lwip/core/ipv4/Makefile delete mode 100644 third_party/lwip/core/ipv4/autoip.c delete mode 100644 third_party/lwip/core/ipv4/icmp.c delete mode 100644 third_party/lwip/core/ipv4/igmp.c delete mode 100644 third_party/lwip/core/ipv4/ip4.c delete mode 100644 third_party/lwip/core/ipv4/ip4_addr.c delete mode 100644 third_party/lwip/core/ipv4/ip_frag.c delete mode 100644 third_party/lwip/core/ipv6/README delete mode 100644 third_party/lwip/core/ipv6/dhcp6.c delete mode 100644 third_party/lwip/core/ipv6/ethip6.c delete mode 100644 third_party/lwip/core/ipv6/icmp6.c delete mode 100644 third_party/lwip/core/ipv6/inet6.c delete mode 100644 third_party/lwip/core/ipv6/ip6.c delete mode 100644 third_party/lwip/core/ipv6/ip6_addr.c delete mode 100644 third_party/lwip/core/ipv6/ip6_frag.c delete mode 100644 third_party/lwip/core/ipv6/mld6.c delete mode 100644 third_party/lwip/core/ipv6/nd6.c delete mode 100644 third_party/lwip/core/mem.c delete mode 100644 third_party/lwip/core/memp.c delete mode 100644 third_party/lwip/core/netif.c delete mode 100644 third_party/lwip/core/pbuf.c delete mode 100644 third_party/lwip/core/raw.c delete mode 100644 third_party/lwip/core/snmp/asn1_dec.c delete mode 100644 third_party/lwip/core/snmp/asn1_enc.c delete mode 100644 third_party/lwip/core/snmp/mib2.c delete mode 100644 third_party/lwip/core/snmp/mib_structs.c delete mode 100644 third_party/lwip/core/snmp/msg_in.c delete mode 100644 third_party/lwip/core/snmp/msg_out.c delete mode 100644 third_party/lwip/core/stats.c delete mode 100644 third_party/lwip/core/sys.c delete mode 100644 third_party/lwip/core/tcp.c delete mode 100644 third_party/lwip/core/tcp_in.c delete mode 100644 third_party/lwip/core/tcp_out.c delete mode 100644 third_party/lwip/core/timers.c delete mode 100644 third_party/lwip/core/udp.c delete mode 100644 third_party/lwip/netif/FILES delete mode 100644 third_party/lwip/netif/Makefile delete mode 100644 third_party/lwip/netif/etharp.c delete mode 100644 third_party/lwip/netif/ethernetif.c delete mode 100644 third_party/lwip/netif/ppp/auth.c delete mode 100644 third_party/lwip/netif/ppp/auth.h delete mode 100644 third_party/lwip/netif/ppp/chap.c delete mode 100644 third_party/lwip/netif/ppp/chap.h delete mode 100644 third_party/lwip/netif/ppp/chpms.c delete mode 100644 third_party/lwip/netif/ppp/chpms.h delete mode 100644 third_party/lwip/netif/ppp/fsm.c delete mode 100644 third_party/lwip/netif/ppp/fsm.h delete mode 100644 third_party/lwip/netif/ppp/ipcp.c delete mode 100644 third_party/lwip/netif/ppp/ipcp.h delete mode 100644 third_party/lwip/netif/ppp/lcp.c delete mode 100644 third_party/lwip/netif/ppp/lcp.h delete mode 100644 third_party/lwip/netif/ppp/magic.c delete mode 100644 third_party/lwip/netif/ppp/magic.h delete mode 100644 third_party/lwip/netif/ppp/md5.c delete mode 100644 third_party/lwip/netif/ppp/md5.h delete mode 100644 third_party/lwip/netif/ppp/pap.c delete mode 100644 third_party/lwip/netif/ppp/pap.h delete mode 100644 third_party/lwip/netif/ppp/ppp.c delete mode 100644 third_party/lwip/netif/ppp/ppp.h delete mode 100644 third_party/lwip/netif/ppp/ppp_impl.h delete mode 100644 third_party/lwip/netif/ppp/ppp_oe.c delete mode 100644 third_party/lwip/netif/ppp/pppdebug.h delete mode 100644 third_party/lwip/netif/ppp/randm.c delete mode 100644 third_party/lwip/netif/ppp/randm.h delete mode 100644 third_party/lwip/netif/ppp/readme.txt delete mode 100644 third_party/lwip/netif/ppp/vj.c delete mode 100644 third_party/lwip/netif/ppp/vj.h delete mode 100644 third_party/lwip/netif/slipif.c delete mode 100644 tools/gen_flashbin.py diff --git a/Makefile b/Makefile index f452b0a..97f6d45 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,131 @@ ifndef PDIR endif -AR = xt-ar -CC = xt-xcc -NM = xt-nm -CPP = xt-cpp -OBJCOPY = xt-objcopy -#MAKE = xt-make +ifeq ($(COMPILE), gcc) + AR = xtensa-lx106-elf-ar + CC = xtensa-lx106-elf-gcc + NM = xtensa-lx106-elf-nm + CPP = xtensa-lx106-elf-cpp + OBJCOPY = xtensa-lx106-elf-objcopy + OBJDUMP = xtensa-lx106-elf-objdump +else + AR = xt-ar + CC = xt-xcc + NM = xt-nm + CPP = xt-cpp + OBJCOPY = xt-objcopy + OBJDUMP = xt-objdump +endif + +BOOT?=none +APP?=0 +SPI_SPEED?=40 +SPI_MODE?=QIO +SPI_SIZE?=512 + +ifeq ($(BOOT), new) + boot = new +else + ifeq ($(BOOT), old) + boot = old + else + boot = none + endif +endif + +ifeq ($(APP), 1) + app = 1 +else + ifeq ($(APP), 2) + app = 2 + else + app = 0 + endif +endif + +ifeq ($(SPI_SPEED), 26.7) + freqdiv = 1 +else + ifeq ($(SPI_SPEED), 20) + freqdiv = 2 + else + ifeq ($(SPI_SPEED), 80) + freqdiv = 15 + else + freqdiv = 0 + endif + endif +endif + + +ifeq ($(SPI_MODE), QOUT) + mode = 1 +else + ifeq ($(SPI_MODE), DIO) + mode = 2 + else + ifeq ($(SPI_MODE), DOUT) + mode = 3 + else + mode = 0 + endif + endif +endif + +# flash larger than 1024KB only use 1024KB to storage user1.bin and user2.bin +ifeq ($(SPI_SIZE), 256) + size = 1 + flash = 256 +else + ifeq ($(SPI_SIZE), 1024) + size = 2 + flash = 1024 + else + ifeq ($(SPI_SIZE), 2048) + size = 3 + flash = 1024 + else + ifeq ($(SPI_SIZE), 4096) + size = 4 + flash = 1024 + else + size = 0 + flash = 512 + endif + endif + endif +endif + +ifeq ($(flash), 512) + ifeq ($(app), 1) + addr = 0x01000 + else + ifeq ($(app), 2) + addr = 0x41000 + endif + endif +else + ifeq ($(flash), 1024) + ifeq ($(app), 1) + addr = 0x01000 + else + ifeq ($(app), 2) + addr = 0x81000 + endif + endif + endif +endif + +LD_FILE = $(LDDIR)/eagle.app.v6.ld + +ifneq ($(boot), none) +ifneq ($(app),0) + LD_FILE = $(LDDIR)/eagle.app.v6.$(boot).$(flash).app$(app).ld + BIN_NAME = user$(app).$(flash).$(boot) +endif +else + app = 0 +endif CSRCS ?= $(wildcard *.c) ASRCs ?= $(wildcard *.s) @@ -82,7 +201,51 @@ endef $(BINODIR)/%.bin: $(IMAGEODIR)/%.out @mkdir -p $(BINODIR) - $(OBJCOPY) -O binary $< $@ + +ifeq ($(APP), 0) + @$(RM) -r ../bin/eagle.S ../bin/eagle.dump + @$(OBJDUMP) -x -s $< > ../bin/eagle.dump + @$(OBJDUMP) -S $< > ../bin/eagle.S +else + @$(RM) -r ../bin/upgrade/$(BIN_NAME).S ../bin/upgrade/$(BIN_NAME).dump + @$(OBJDUMP) -x -s $< > ../bin/upgrade/$(BIN_NAME).dump + @$(OBJDUMP) -S $< > ../bin/upgrade/$(BIN_NAME).S +endif + + @$(OBJCOPY) --only-section .text -O binary $< eagle.app.v6.text.bin + @$(OBJCOPY) --only-section .data -O binary $< eagle.app.v6.data.bin + @$(OBJCOPY) --only-section .rodata -O binary $< eagle.app.v6.rodata.bin + @$(OBJCOPY) --only-section .irom0.text -O binary $< eagle.app.v6.irom0text.bin + + @echo "" + @echo "!!!" + +ifeq ($(app), 0) + @python ../tools/gen_appbin.py $< 0 $(mode) $(freqdiv) $(size) + @mv eagle.app.flash.bin ../bin/eagle.flash.bin + @mv eagle.app.v6.irom0text.bin ../bin/eagle.irom0text.bin + @rm eagle.app.v6.* + @echo "No boot needed." + @echo "Generate eagle.flash.bin and eagle.irom0text.bin successully in folder bin." + @echo "eagle.flash.bin-------->0x00000" + @echo "eagle.irom0text.bin---->0x40000" +else + ifeq ($(boot), new) + @python ../tools/gen_appbin.py $< 2 $(mode) $(freqdiv) $(size) + @echo "Support boot_v1.2 and +" + else + @python ../tools/gen_appbin.py $< 1 $(mode) $(freqdiv) $(size) + @echo "Support boot_v1.1 and +" + endif + + @mv eagle.app.flash.bin ../bin/upgrade/$(BIN_NAME).bin + @rm eagle.app.v6.* + @echo "Generate $(BIN_NAME).bin successully in folder bin/upgrade." + @echo "boot.bin------------>0x00000" + @echo "$(BIN_NAME).bin--->$(addr)" +endif + + @echo "!!!" ############################################################# # Rules base diff --git a/app/Makefile b/app/Makefile index 11be1da..9a1d133 100644 --- a/app/Makefile +++ b/app/Makefile @@ -45,16 +45,6 @@ ifeq ($(FLAVOR),release) TARGET_LDFLAGS += -g -O0 endif -LD_FILE = $(LDDIR)/eagle.app.v6.ld - -ifeq ($(APP), 1) - LD_FILE = $(LDDIR)/eagle.app.v6.app1.ld -endif - -ifeq ($(APP), 2) - LD_FILE = $(LDDIR)/eagle.app.v6.app2.ld -endif - COMPONENTS_eagle.app.v6 = \ user/libuser.a @@ -124,15 +114,6 @@ INCLUDES := $(INCLUDES) -I $(PDIR)include PDIR := ../$(PDIR) sinclude $(PDIR)Makefile -######################################################################### -# -# generate bin file -# - -$(BINODIR)/%.bin: $(IMAGEODIR)/%.out - @mkdir -p $(BINODIR) - $(OBJCOPY) -O binary $< $@ - .PHONY: FORCE FORCE: diff --git a/app/gen_misc.bat b/app/gen_misc.bat index e65b693..216a3c8 100644 --- a/app/gen_misc.bat +++ b/app/gen_misc.bat @@ -1,27 +1,121 @@ @echo off -set BACKPATH=%PATH% -set PATH=%BACKPATH%;%CD%\..\tools -@echo on -del /F ..\bin\eagle.app.v6.flash.bin ..\bin\eagle.app.v6.irom0text.bin ..\bin\eagle.app.v6.dump ..\bin\eagle.app.v6.S +echo Please follow below steps(1-5) to generate specific bin(s): +echo STEP 1: choose boot version(0=boot_v1.1, 1=boot_v1.2+, 2=none) +set input=default +set /p input=enter(0/1/2, default 2): -cd .output\eagle\debug\image +if %input% equ 0 ( + set boot=old +) else ( +if %input% equ 1 ( + set boot=new +) else ( + set boot=none +) +) -xt-objdump -x -s eagle.app.v6.out > ..\..\..\..\..\bin\eagle.app.v6.dump -xt-objdump -S eagle.app.v6.out > ..\..\..\..\..\bin\eagle.app.v6.S +echo boot mode: %boot% +echo. -xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin -xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin -xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin -xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin +echo STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin) +set input=default +set /p input=enter (0/1/2, default 0): -gen_appbin.py eagle.app.v6.out v6 +if %input% equ 1 ( + if %boot% equ none ( + set app=0 + echo choose no boot before + echo generate bin: eagle.flash.bin+eagle.irom0text.bin + ) else ( + set app=1 + echo generate bin: user1.bin + ) +) else ( +if %input% equ 2 ( + if %boot% equ none ( + set app=0 + echo choose no boot before + echo generate bin: eagle.flash.bin+eagle.irom0text.bin + ) else ( + set app=2 + echo generate bin: user2.bin + ) +) else ( + if %boot% neq none ( + set boot=none + echo ignore boot + ) + set app=0 + echo generate bin: eagle.flash.bin+eagle.irom0text.bin +)) -xcopy /y eagle.app.v6.irom0text.bin ..\..\..\..\..\bin\ -xcopy /y eagle.app.v6.flash.bin ..\..\..\..\..\bin\ +echo. -cd ..\..\..\..\ +echo STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz) +set input=default +set /p input=enter (0/1/2/3, default 2): + +if %input% equ 0 ( + set spi_speed=20 +) else ( +if %input% equ 1 ( + set spi_speed=26.7 +) else ( +if %input% equ 3 ( + set spi_speed=80 +) else ( + set spi_speed=40 +))) + +echo spi speed: %spi_speed% MHz +echo. + +echo STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT) +set input=default +set /p input=enter (0/1/2/3, default 0): + +if %input% equ 1 ( + set spi_mode=QOUT +) else ( +if %input% equ 2 ( + set spi_mode=DIO +) else ( +if %input% equ 3 ( + set spi_mode=DOUT +) else ( + set spi_mode=QIO +))) + +echo spi mode: %spi_mode% +echo. + +echo STEP 5: choose spi size(0=256KB, 1=512KB, 2=1024KB, 3=2048KB, 4=4096KB) +set input=default +set /p input=enter (0/1/2/3/4, default 1): + +if %input% equ 0 ( + set spi_size=256 +) else ( +if %input% equ 2 ( + set spi_size=1024 +) else ( +if %input% equ 3 ( + set spi_size=2048 +) else ( +if %input% equ 4 ( + set spi_size=4096 +) else ( + set spi_size=512 +)))) + +echo spi size: %spi_size% KB + +touch user/user_main.c + +echo. +echo start... +echo. + +make BOOT=%boot% APP=%app% SPI_SPEED=%spi_speed% SPI_MODE=%spi_mode% SPI_SIZE=%spi_size% -@echo off -set PATH=%BACKPATH% -@echo on diff --git a/app/gen_misc.sh b/app/gen_misc.sh index eeada23..017dad0 100644 --- a/app/gen_misc.sh +++ b/app/gen_misc.sh @@ -1,25 +1,126 @@ -#!/bin/bash -x -make -if [ $? == 0 ];then -rm ../bin/eagle.app.v6.flash.bin ../bin/eagle.app.v6.irom0text.bin ../bin/eagle.app.v6.dump ../bin/eagle.app.v6.S +#!/bin/bash -cd .output/eagle/debug/image +echo "Please follow below steps(1-5) to generate specific bin(s):" +echo "STEP 1: choose boot version(0=boot_v1.1, 1=boot_v1.2+, 2=none)" +echo "enter(0/1/2, default 2):" +read input -xt-objdump -x -s eagle.app.v6.out > ../../../../../bin/eagle.app.v6.dump -xt-objdump -S eagle.app.v6.out > ../../../../../bin/eagle.app.v6.S +if [ -z "$input" ]; then + boot=none +elif [ $input == 0 ]; then + boot=old +elif [ $input == 1 ]; then + boot=new +else + boot=none +fi + +echo "boot mode: $boot" +echo "" + +echo "STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)" +echo "enter (0/1/2, default 0):" +read input + +if [ -z "$input" ]; then + if [ $boot != none ]; then + boot=none + echo "ignore boot" + fi + app=0 + echo "generate bin: eagle.flash.bin+eagle.irom0text.bin" +elif [ $input == 1 ]; then + if [ $boot == none ]; then + app=0 + echo "choose no boot before" + echo "generate bin: eagle.flash.bin+eagle.irom0text.bin" + else + app=1 + echo "generate bin: user1.bin" + fi +elif [ $input == 2 ]; then + if [ $boot == none ]; then + app=0 + echo "choose no boot before" + echo "generate bin: eagle.flash.bin+eagle.irom0text.bin" + else + app=2 + echo "generate bin: user2.bin" + fi +else + if [ $boot != none ]; then + boot=none + echo "ignore boot" + fi + app=0 + echo "generate bin: eagle.flash.bin+eagle.irom0text.bin" +fi + +echo "" -xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin -xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin -xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin -xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin +echo "STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)" +echo "enter (0/1/2/3, default 2):" +read input -../../../../../tools/gen_appbin.py eagle.app.v6.out v6 +if [ -z "$input" ]; then + spi_speed=40 +elif [ $input == 0 ]; then + spi_speed=20 +elif [ $input == 1 ]; then + spi_speed=26.7 +elif [ $input == 3 ]; then + spi_speed=80 +else + spi_speed=40 +fi -cp eagle.app.v6.irom0text.bin ../../../../../bin/ -cp eagle.app.v6.flash.bin ../../../../../bin/ +echo "spi speed: $spi_speed MHz" +echo "" -cd ../../../../../ +echo "STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)" +echo "enter (0/1/2/3, default 0):" +read input +if [ -z "$input" ]; then + spi_mode=QIO +elif [ $input == 1 ]; then + spi_mode=QOUT +elif [ $input == 2 ]; then + spi_mode=DIO +elif [ $input == 3 ]; then + spi_mode=DOUT else -echo "make error" + spi_mode=QIO fi + +echo "spi mode: $spi_mode" +echo "" + +echo "STEP 5: choose spi size(0=256KB, 1=512KB, 2=1024KB, 3=2048KB, 4=4096KB)" +echo "enter (0/1/2/3/4, default 1):" +read input + +if [ -z "$input" ]; then + spi_size=512 +elif [ $input == 0 ]; then + spi_size=256 +elif [ $input == 2 ]; then + spi_size=1024 +elif [ $input == 3 ]; then + spi_size=2048 +elif [ $input == 4 ]; then + spi_size=4096 +else + spi_size=512 +fi + +echo "spi size: $spi_size KB" +echo "" + +touch user/user_main.c + +echo "" +echo "start..." +echo "" + +make COMPILE=gcc BOOT=$boot APP=$app SPI_SPEED=$spi_speed SPI_MODE=$spi_mode SPI_SIZE=$spi_size diff --git a/app/gen_misc_plus.bat b/app/gen_misc_plus.bat deleted file mode 100644 index 8022305..0000000 --- a/app/gen_misc_plus.bat +++ /dev/null @@ -1,27 +0,0 @@ -@echo off -set BACKPATH=%PATH% -set PATH=%BACKPATH%;%CD%\..\tools -@echo on - -rm ..\bin\upgrade\%1.bin - -cd .output\eagle\debug\image\ - -xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin -xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin -xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin -xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin - -gen_appbin.py eagle.app.v6.out v6 - -gen_flashbin.py eagle.app.v6.flash.bin eagle.app.v6.irom0text.bin - -cp eagle.app.flash.bin %1.bin - -xcopy /y %1.bin ..\..\..\..\..\bin\upgrade\ - -cd ..\..\..\..\ - -@echo off -set PATH=%BACKPATH% -@echo on \ No newline at end of file diff --git a/app/gen_misc_plus.sh b/app/gen_misc_plus.sh deleted file mode 100644 index dba8a5b..0000000 --- a/app/gen_misc_plus.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -x -make APP=$1 -if [ $? == 0 ];then -rm ../bin/upgrade/user$1.bin - -cd .output/eagle/debug/image/ - -xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin -xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin -xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin -xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin - -../../../../../tools/gen_appbin.py eagle.app.v6.out v6 - -../../../../../tools/gen_flashbin.py eagle.app.v6.flash.bin eagle.app.v6.irom0text.bin - -cp eagle.app.flash.bin user$1.bin -cp user$1.bin ../../../../../bin/upgrade/ - -cd ../../../../../ - -else -echo "make error" -fi \ No newline at end of file diff --git a/app/readme.txt b/app/readme.txt new file mode 100644 index 0000000..3e243ed --- /dev/null +++ b/app/readme.txt @@ -0,0 +1,40 @@ +1ˇ˘compile options + +(1) COMPILE + Possible value: gcc + Default value: + If not set, use xt-xcc by default. + +(2) BOOT + Possible value: none/old/new + none: no need boot + old: use boot_v1.1 + new: use boot_v1.2 + Default value: none + +(3) APP + Possible value: 0/1/2 + 0: original mode, generate eagle.app.v6.flash.bin and eagle.app.v6.irom0text.bin + 1: generate user1 + 2: generate user2 + Default value: 0 + +(3) SPI_SPEED + Possible value: 20/26.7/40/80 + Default value: 40 + +(4) SPI_MODE + Possible value: QIO/QOUT/DIO/DOUT + Default value: QIO + +(4) SPI_SIZE + Possible value: 256/512/1024/2048/4096 + Default value: 512 + +For example: + make COMPILE=gcc BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE=512 + +2ˇ˘You can also use gen_misc to make and generate specific bin you needed. + Linux: ./gen_misc.sh + Windows: gen_misc.bat + Follow the tips and steps. \ No newline at end of file diff --git a/app/user/user_main.c b/app/user/user_main.c index 6f46a8b..88de521 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -152,10 +152,7 @@ void task3(void *pvParameters) void ICACHE_FLASH_ATTR user_init(void) { - printf("SDK version:%d.%d.%d\n", - SDK_VERSION_MAJOR, - SDK_VERSION_MINOR, - SDK_VERSION_REVISION); + printf("SDK version:%s\n", system_get_sdk_version()); /* need to set opmode before you set config */ wifi_set_opmode(STATIONAP_MODE); diff --git a/bin/boot_v1.3(b3).bin b/bin/boot_v1.3(b3).bin new file mode 100644 index 0000000000000000000000000000000000000000..c2d7027a3e8eb0ea79e86c7157f9cdff40bce7a6 GIT binary patch literal 2544 zcma)8e{56N6~5;tF}8WG-*Yk-h&%3kv?QW!DTLM3?ag@w<7%AdeE`F+}JV zHxx^X#KvSt^V#QC)sG~CnAsRt#;0_@3&zFhVIWeuq$S7Xvo9HodIDKFbd@iyF*F5E z6;?4OZvbL9a{DB|%~TVJ%ZR9l10;uv6=ftH z%RbnTZBnzkb}>O>mb7~B&yj^SD;kj-Bf=X-Z#lM9vveXHG-jz+I9w5<4(Iti+_MHm zAvY}{RnZ0E%q@e+^kK5^3RiD%ctwPk-QYIdr>{8l3-g;|2Pg?ss^R`dlrBv%G{zR; z_K_&&>_WVW_!;6I#Ges4#1#bTz4MUVg)Q!L6nP3!jaZIoK6ftTPZCr@#K9^bZh{Z6l`paB))HtffS8aBvvdttp+k2SNVYm@Jpv_yEFMDNNI2# z&RobGkh^xt$&fU9fTcsOvQb`pl&eR1`1njiDXbC_3NEs^wB-WbbRkGKh2HN}f5|~9 z_DDppc;!e{l=f+4aXB4UiMpSIQ*^3J`08Pv@ccv!xo?u&?je)mO;OL6YT0={Mb+0h z5D{1JAfzQU`A?iuRf|*0Dyyfk8{e2ORa9Z%J`P{q5ea*F&;|bx)bHA7Vd_OVwZgm> zke5P}4Pu}VKjp&|r=qEQID8>uPnr`u>`jHsLha=6x$q4g$=CmyPf2Q$!)L;g9?b9e zl^s5k|J6Y{p2@HEGc|;-vo|^Rg*(b z@ZmnL{+Ppv;1A~d2Dth(hYy8iOBK{Zg4d(sM;ty7d?=F_qGk@iFPMXQTMsAr;JLTs z-Fbrwv#)p@{g!RNORH`$XwQd+)#AQ!uxa8U~{-K z2)BbNSnL8 zH($SGmTNg?AJj~bw^6+fLz%z(oa1JN|1N1x(p3HiE-Kv}QTK4+qQei+FO6ZtFEzueim>8^0d1QooJ#n?Jh-*W&9@QWnI@(0aLNb;;S^R!?S#y3ky+P zMoV!S`833N0PE6=F-!fU5ew@XI4%cF4;C{B7+)RgZ}K_2&5G-)(l9v9=av#9wPvvS z#CiMc*e%h%kGVu~SNaZc@1L&W-X=qTy_@!(;Q#0L?mBd){#|?_#Q%naKuA29>`1b0 zW=q><)kz3Rv?X_@SgfNX#S&)Q*7%khmW&t7R0m5X_pq3``Pt6xEY{ZU6!Bl1-wKu$ z{LVo8mSURc;>n$D9qsG^R=cdOI##zt$OvN_8`rRn+vD*qtnfU*$Z~yM!}Hi1Sh4K> zB11mwTRGbnb?Z8|#EUXaOPbcKFXU`m|Kt;bL}u;CtX#FE zj5XV?oNa4Y6iMA|M=Flx*oMQ^FcNQXHQP7Gi_4nzJ=3{uJHyf>JKIG~n8EJbEei18 n{?488q*!w;b_5W`nhS41lGtjt)G+bOffB5j>!Gi}6x&HtE!SLfpke`=}g9QTvBO?TA literal 0 HcmV?d00001 diff --git a/include/espressif/c_types.h b/include/espressif/c_types.h index 902fb9b..ab788cc 100644 --- a/include/espressif/c_types.h +++ b/include/espressif/c_types.h @@ -74,6 +74,7 @@ typedef enum { #ifdef ICACHE_FLASH #define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text"))) +#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text"))) #else #define ICACHE_FLASH_ATTR #endif /* ICACHE_FLASH */ diff --git a/include/espressif/esp_common.h b/include/espressif/esp_common.h index dfb111e..266166f 100644 --- a/include/espressif/esp_common.h +++ b/include/espressif/esp_common.h @@ -9,15 +9,13 @@ #include "c_types.h" #include "esp_libc.h" #include "esp_misc.h" +#include "esp_wifi.h" #include "esp_softap.h" #include "esp_sta.h" #include "esp_system.h" #include "esp_timer.h" -#include "esp_wifi.h" #include "esp8266/esp8266.h" #include "spi_flash.h" - -#include "version.h" #endif diff --git a/include/espressif/esp_libc.h b/include/espressif/esp_libc.h index 1c2f05c..963fec5 100644 --- a/include/espressif/esp_libc.h +++ b/include/espressif/esp_libc.h @@ -49,4 +49,16 @@ void *realloc(void *p, size_t n); int atoi(const char *s); long atol(const char *s); +/* NOTE: don't use printf_opt in irq handler, for test */ +#define printf_opt(fmt, ...) do { \ + static const char flash_str[] ICACHE_RODATA_ATTR = fmt; \ + printf(flash_str, ##__VA_ARGS__); \ + } while(0) + +/* NOTE: don't use printf_opt in irq handler, for test */ +#define sprintf_opt(out, fmt, ...) do { \ + static const char flash_str[] ICACHE_RODATA_ATTR = fmt; \ + sprintf(out, flash_str, ##__VA_ARGS__); \ + } while(0) + #endif /* __LIBC_H__ */ diff --git a/include/espressif/esp_softap.h b/include/espressif/esp_softap.h index 6330141..4d3a434 100644 --- a/include/espressif/esp_softap.h +++ b/include/espressif/esp_softap.h @@ -6,22 +6,15 @@ #ifndef __ESP_SOFTAP_H__ #define __ESP_SOFTAP_H__ -typedef enum _auth_mode { - AUTH_OPEN = 0, - AUTH_WEP, - AUTH_WPA_PSK, - AUTH_WPA2_PSK, - AUTH_WPA_WPA2_PSK -} AUTH_MODE; - struct softap_config { uint8 ssid[32]; uint8 password[64]; uint8 ssid_len; uint8 channel; - uint8 authmode; + AUTH_MODE authmode; uint8 ssid_hidden; uint8 max_connection; + uint16 beacon_interval; }; bool wifi_softap_get_config(struct softap_config *config); diff --git a/include/espressif/esp_sta.h b/include/espressif/esp_sta.h index b4351b9..06ea288 100644 --- a/include/espressif/esp_sta.h +++ b/include/espressif/esp_sta.h @@ -35,7 +35,7 @@ struct bss_info { uint8 ssid[32]; uint8 channel; sint8 rssi; - uint8 authmode; + AUTH_MODE authmode; uint8 is_hidden; }; diff --git a/include/espressif/esp_system.h b/include/espressif/esp_system.h index cf3f830..7e897d6 100644 --- a/include/espressif/esp_system.h +++ b/include/espressif/esp_system.h @@ -9,21 +9,27 @@ #include "c_types.h" enum rst_reason { - DEFAULT_RST_FLAG = 0, - WDT_RST_FLAG = 1, - EXP_RST_FLAG = 2 + DEFAULT_RST = 0, + WDT_RST = 1, + EXCEPTION_RST = 2, + SOFT_RST = 3 }; struct rst_info{ - uint32 flag; + uint32 reason; uint32 exccause; uint32 epc1; uint32 epc2; uint32 epc3; uint32 excvaddr; uint32 depc; + uint32 rtn_addr; }; +struct rst_info* system_get_rst_info(void); + +const char* system_get_sdk_version(void); + void system_restore(void); void system_restart(void); void system_deep_sleep(uint32 time_in_us); diff --git a/include/espressif/esp_wifi.h b/include/espressif/esp_wifi.h index 70614f2..a959e88 100644 --- a/include/espressif/esp_wifi.h +++ b/include/espressif/esp_wifi.h @@ -14,6 +14,15 @@ enum { MAX_MODE }; +typedef enum _auth_mode { + AUTH_OPEN = 0, + AUTH_WEP, + AUTH_WPA_PSK, + AUTH_WPA2_PSK, + AUTH_WPA_WPA2_PSK, + AUTH_MAX +} AUTH_MODE; + uint8 wifi_get_opmode(void); bool wifi_set_opmode(uint8 opmode); diff --git a/include/espressif/spi_flash.h b/include/espressif/spi_flash.h index 14890e1..0bb2edd 100644 --- a/include/espressif/spi_flash.h +++ b/include/espressif/spi_flash.h @@ -14,6 +14,9 @@ typedef enum { #define SPI_FLASH_SEC_SIZE 4096 +uint32 spi_flash_get_id(void); +SpiFlashOpResult spi_flash_read_status(uint32 *status); +SpiFlashOpResult spi_flash_write_status(uint32 status_value); SpiFlashOpResult spi_flash_erase_sector(uint16 sec); SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size); SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size); diff --git a/include/espressif/version.h b/include/espressif/version.h deleted file mode 100644 index 4d3540f..0000000 --- a/include/espressif/version.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __SDK_VERSION_H__ -#define __SDK_VERSION_H__ - -#define SDK_VERSION_MAJOR 0U -#define SDK_VERSION_MINOR 0U -#define SDK_VERSION_REVISION 5U -#define SDK_VERSION_INTERNAL 0U - -#define SDK_VERSION (SDK_VERSION_MAJOR << 16 | SDK_VERSION_MINOR << 8 | SDK_VERSION_REVISION) - -#endif - diff --git a/include/freertos/FreeRTOSConfig.h b/include/freertos/FreeRTOSConfig.h index 87164f0..5c22444 100644 --- a/include/freertos/FreeRTOSConfig.h +++ b/include/freertos/FreeRTOSConfig.h @@ -82,8 +82,8 @@ #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( ( unsigned long ) 80000000 ) #define configTICK_RATE_HZ ( ( portTickType ) 100 ) -#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 21 ) -#define configMINIMAL_STACK_SIZE ( ( unsigned short )128 ) +#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 15 ) +#define configMINIMAL_STACK_SIZE ( ( unsigned short )156 ) //#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_TRACE_FACILITY 0 diff --git a/include/freertos/portmacro.h b/include/freertos/portmacro.h index 7d9da7f..93f4e1b 100644 --- a/include/freertos/portmacro.h +++ b/include/freertos/portmacro.h @@ -107,7 +107,6 @@ typedef unsigned int INT32U; /* Scheduler utilities. */ extern void PendSV(char req); -extern char ClosedLv1Isr; //#define portYIELD() vPortYield() #define portYIELD() PendSV(1) @@ -116,20 +115,29 @@ extern char ClosedLv1Isr; // if(xSwitchRequired) PendSV(1) #define HDL_MAC_SIG_IN_LV1_ISR() PendSV(2) + +/* Task utilities. */ +#define portEND_SWITCHING_ISR( xSwitchRequired ) \ +{ \ +extern void vTaskSwitchContext( void ); \ + \ + if( xSwitchRequired ) \ + { \ + vTaskSwitchContext(); \ + } \ +} + + /*-----------------------------------------------------------*/ +extern unsigned cpu_sr; /* Critical section management. */ -extern void portDISABLE_INTERRUPTS( void ); -extern void portENABLE_INTERRUPTS( void ); extern void vPortEnterCritical( void ); extern void vPortExitCritical( void ); -extern unsigned cpu_sr; - -/* Don't modify these lines. This port requires OS_CRITICAL_METHOD 3. */ -#define OS_CRITICAL_METHOD 3 - -#if OS_CRITICAL_METHOD == 3 +//DYC_ISR_DBG +void PortDisableInt_NoNest( void ); +void PortEnableInt_NoNest( void ); /* Disable interrupts, saving previous state in cpu_sr */ #define portDISABLE_INTERRUPTS() \ @@ -137,32 +145,14 @@ extern unsigned cpu_sr; /* Restore interrupts to previous level saved in cpu_sr */ #define portENABLE_INTERRUPTS() __asm__ volatile ("wsr %0, ps" :: "a" (cpu_sr) : "memory") -#endif -//DYC_ISR_DBG -void ICACHE_FLASH_ATTR vPortEnterCritical1( void ); -void ICACHE_FLASH_ATTR vPortExitCritical1( void ); -#define portSET_INTERRUPT_MASK_FROM_ISR() vPortEnterCritical1() -#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortExitCritical1() - -//#define portDISABLE_INTERRUPTS() ets_intr_lock() -//#define portENABLE_INTERRUPTS() ets_intr_unlock() - -//#define portDISABLE_INTERRUPTS() -//#define portENABLE_INTERRUPTS() #define portENTER_CRITICAL() vPortEnterCritical() #define portEXIT_CRITICAL() vPortExitCritical() -/* Task utilities. */ -#define portEND_SWITCHING_ISR( xSwitchRequired ) \ -{ \ -extern void vTaskSwitchContext( void ); \ - \ - if( xSwitchRequired ) \ - { \ - vTaskSwitchContext(); \ - } \ -} +// no need to disable/enable lvl1 isr again in ISR +//#define portSET_INTERRUPT_MASK_FROM_ISR() PortDisableInt_NoNest() +//#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) PortEnableInt_NoNest() + /*-----------------------------------------------------------*/ diff --git a/include/lwip/lwip/api.h b/include/lwip/lwip/api.h index a8add39..b5d0a55 100644 --- a/include/lwip/lwip/api.h +++ b/include/lwip/lwip/api.h @@ -80,6 +80,9 @@ extern "C" { #define NETCONN_FLAG_IPV6_V6ONLY 0x20 #endif /* LWIP_IPV6 */ + //***********Code for WIFI_BLOCK from upper************** +#define NETCONN_FLAG_RECV_HOLD 0x80 + /* Helpers to process several netconn_types by the same code */ #define NETCONNTYPE_GROUP(t) ((t)&0xF0) @@ -208,6 +211,8 @@ struct netconn { /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ u8_t flags; #if LWIP_TCP + //***********Code for WIFI_BLOCK from upper************** + u32_t recv_holded_buf_Len; /** 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; diff --git a/include/lwip/lwipopts.h b/include/lwip/lwipopts.h index 0da8345..917668e 100644 --- a/include/lwip/lwipopts.h +++ b/include/lwip/lwipopts.h @@ -107,6 +107,34 @@ ---------- IP options ---------- -------------------------------- */ +/** + * 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. + */ +#define IP_REASSEMBLY 0 + +/** + * 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. + */ +#define IP_FRAG 1 + +/** + * 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. + */ +#define IP_REASS_MAXAGE 3 + +/** + * 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! + */ +#define IP_REASS_MAX_PBUFS 10 /* ---------------------------------- @@ -182,7 +210,7 @@ * LWIP_CALLBACK_API==1: The PCB callback function is called directly * for the event. This is the default. */ -#define TCP_MSS 1024 +#define TCP_MSS 1460 /** * TCP_MAXRTX: Maximum number of retransmissions of data segments. @@ -246,7 +274,7 @@ * The priority value itself is platform-dependent, but is passed to * sys_thread_new() when the thread is created. */ -#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES-4) +#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES-5) /** * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages diff --git a/include/udhcp/common.h b/include/udhcp/common.h index 14caa1c..332cf76 100644 --- a/include/udhcp/common.h +++ b/include/udhcp/common.h @@ -388,7 +388,7 @@ void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC; #ifdef UDHCP_DBG #define UDHCP_DEBUG os_printf #else -#define UDHCP_DEBUG +#define UDHCP_DEBUG(...) #endif /*** Other shared functions ***/ diff --git a/ld/eagle.app.v6.app1.ld b/ld/eagle.app.v6.new.1024.app1.ld similarity index 75% rename from ld/eagle.app.v6.app1.ld rename to ld/eagle.app.v6.new.1024.app1.ld index 4b624b1..41514ba 100644 --- a/ld/eagle.app.v6.app1.ld +++ b/ld/eagle.app.v6.new.1024.app1.ld @@ -2,10 +2,10 @@ /* Linker Script for ld -N */ MEMORY { - dport0_0_seg : org = 0x3FF00000, len = 0x10 - dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 - irom0_0_seg : org = 0x40211000, len = 0x2B000 + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40201010, len = 0x6B000 } PHDRS @@ -20,6 +20,11 @@ PHDRS /* Default entry point: */ ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) PROVIDE(_memmap_vecbase_reset = 0x40000000); /* Various memory-map dependent cache attribute settings: */ _memmap_cacheattr_wb_base = 0x00000110; @@ -121,6 +126,13 @@ SECTIONS _rodata_end = ABSOLUTE(.); } >dram0_0_seg :dram0_0_phdr + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + .bss ALIGN(8) (NOLOAD) : ALIGN(4) { . = ALIGN (8); @@ -149,6 +161,30 @@ SECTIONS { _stext = .; _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); *(.entry.text) *(.init.literal) *(.init) diff --git a/ld/eagle.app.v6.new.1024.app2.ld b/ld/eagle.app.v6.new.1024.app2.ld new file mode 100644 index 0000000..b751653 --- /dev/null +++ b/ld/eagle.app.v6.new.1024.app2.ld @@ -0,0 +1,217 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40281010, len = 0x6B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.new.512.app1.ld b/ld/eagle.app.v6.new.512.app1.ld new file mode 100644 index 0000000..66e8c7b --- /dev/null +++ b/ld/eagle.app.v6.new.512.app1.ld @@ -0,0 +1,217 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40201010, len = 0x2B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.new.512.app2.ld b/ld/eagle.app.v6.new.512.app2.ld new file mode 100644 index 0000000..89773ac --- /dev/null +++ b/ld/eagle.app.v6.new.512.app2.ld @@ -0,0 +1,217 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40241010, len = 0x2B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.old.1024.app1.ld b/ld/eagle.app.v6.old.1024.app1.ld new file mode 100644 index 0000000..e17350e --- /dev/null +++ b/ld/eagle.app.v6.old.1024.app1.ld @@ -0,0 +1,217 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40211000, len = 0x6B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.old.1024.app2.ld b/ld/eagle.app.v6.old.1024.app2.ld new file mode 100644 index 0000000..80d77cb --- /dev/null +++ b/ld/eagle.app.v6.old.1024.app2.ld @@ -0,0 +1,217 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40291000, len = 0x6B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.old.512.app1.ld b/ld/eagle.app.v6.old.512.app1.ld new file mode 100644 index 0000000..7b71083 --- /dev/null +++ b/ld/eagle.app.v6.old.512.app1.ld @@ -0,0 +1,217 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40211000, len = 0x2B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.app2.ld b/ld/eagle.app.v6.old.512.app2.ld similarity index 77% rename from ld/eagle.app.v6.app2.ld rename to ld/eagle.app.v6.old.512.app2.ld index 7f45370..33959a4 100644 --- a/ld/eagle.app.v6.app2.ld +++ b/ld/eagle.app.v6.old.512.app2.ld @@ -2,9 +2,9 @@ /* Linker Script for ld -N */ MEMORY { - dport0_0_seg : org = 0x3FF00000, len = 0x10 - dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40251000, len = 0x2B000 } @@ -20,6 +20,11 @@ PHDRS /* Default entry point: */ ENTRY(call_user_start) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) PROVIDE(_memmap_vecbase_reset = 0x40000000); /* Various memory-map dependent cache attribute settings: */ _memmap_cacheattr_wb_base = 0x00000110; @@ -121,6 +126,13 @@ SECTIONS _rodata_end = ABSOLUTE(.); } >dram0_0_seg :dram0_0_phdr + .UserExceptionVector.literal : AT(LOADADDR(.rodata) + (ADDR(.UserExceptionVector.literal) - ADDR(.rodata))) ALIGN(4) + { + _UserExceptionVector_literal_start = ABSOLUTE(.); + *(.UserExceptionVector.literal) + _UserExceptionVector_literal_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + .bss ALIGN(8) (NOLOAD) : ALIGN(4) { . = ALIGN (8); @@ -149,6 +161,30 @@ SECTIONS { _stext = .; _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); *(.entry.text) *(.init.literal) *(.init) diff --git a/lib/libfreertos.a b/lib/libfreertos.a index 671b132ee6de02e218d6b5052d32af09f92c906d..d8f8448c84341a43726996678804b96a520b8f34 100644 GIT binary patch delta 24010 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 N<`u^a*?B8H?^a$5$_5< zNqC~-(cdGC7W{y)CixI+IS3tb;9MprGFqzuJ+?*maYO_jD2|L0%7u!9`;wy^8M&o} ze3Ihjh50lHyV-t5L=Wc9hI~@jMk1OGlM$F86}nx(&0oJT4F!Ygn`G1);oTZXKC8sL z7JWvT#S{HYX?-eu0=F2FphFR55>W7GX;G+g3niM};%Ou_dm|dRh@#0xc(x*_z#H=2WK4oig;y$`9zp!K3cHDz_}tk3NtbZ=tyo$d6&|N} z=LoYOVE#Qz82!vxxP?ed5X_UnO|l5?RJc)VEix+bh{A4yC4Qa4%%$`w!fYDcJV=q{fqc_82|f!%oG0ejNmhg&u!bIZwlL#hcCl* zMsRr9W_)-%XLxKxL!DjP@aoVsY?>TDRSo6vWU%@$j*l&VhP^djrxU%cNQXB^a0ggA z+(wzxyd1&nBY0f|uZ`d}V9TdX4Z1I*6~Tt5lEJ~x_D)rc?V}!wn;`>d%4ahVRzLCn z&2WqSC5FlEfe25JN54Y;$A;VFe`%O%ePftvog=OEsqi-*?7K2{eOtuI}u=|uc$9Vb!2SNAE z_e$g8!0wxGaYS>EY2d(4^Of;BpQXHf)smXh2kH%TCcD8f@9bv7gVogc80H-Iq~T}e zuQ5DYnk_28J=pWc!-3s{y%zF%|8EOyjU-?`r$TUGmwPoVPYa7&Hna5qin52Gk`BIDt}apN1&u%DEB+B9(B zxbe*Ultss#B_q#pQu@6`K!RPS+l}Y+*iA;_@Kk9yDW4AxuBqo64+nNFcb)Nc^zeg3 z-*iQ9&QSSe3aP-p1ov42hRjr%Yw9PAhXcE&4nI!V>v-NYaA2qTqw$$qPK#>uVcywF8n%Xe5IyJH-Qh0;3U{BrR?^ch;5_8 zY%?8ZAM5aH?JnHR&d%NOoCX|Tq20N|%Om*S2u>}H1n!RDMG<^U1amxbnQ}&QxHEz| zv^l;pf)f#(oyKAR3y+H~!BTB?9KJh(IgUEMIf6OwI-c{f!zEgL9WIXGei7WKTlIbs zR|PHsR|^jFrNiOyb4gb4qqPHjipA9to7sNK+TpPj+t5Dh6>t|B=1b2)!}aoSG0fF5 zA7IdvYnx$8`M2)2%-;On3j(FvH** z!{PYQIHCZ?O;5v&r_qKPXB!MN{ch8fiCh0%m( zN)3K6lR&*;CWpy}nKfDrGn@Rvux6P;?@4@~Qq8xIGL z8_%anDucdJ<_}8if1T!2AxXgfurmCDGB_<-mllP;&;h&OE^Zcf8Sy2M0^qWPs9j(@!{>6tT+=0m-B)7b!X}pwc3*(Q&yTj| z-Des&aJZ^43x?kxP4tk7;J|L_X)~TNUM+J=&r8O`f%mO>#xpN$HOz(O-wf-g*x^^e zu;&t}QG}mNHy5{-JPjN;ZhSB4;NeIE<|gxy2(DF}&hVidYCIg+{eSW5gss(6-pM9{ z1iNM*i->ywx#^~X1H0zE7foeav(7Yd;JES2(OBztmz=7l9xkby>4CV3}E=+5|fn7d*Qi!Z}$C{{*e6V}>{m@up4W@wu zyTYzP!%;wPv1#DIu0?-kJh!7y8cuO-y2b+FW|?bt=3Z(I2X@Vl4xX#nEnx0u<-!|M z@mxkGY)XauMjF>A@{ETQ*3XTsvy8C_u1Y5u<_h&x!(5wk)#@LaE^NB-aNxM{B@xXW z)4+k9W+j@DczY2d(4(-zTuiH3jt z07<}!);;1-4`^|>Ah;Ea) zao=e?9N3NfH4)7o)4+k9CNr+`XTbJR$apOlD&ze5QFt^*qGMBdA zcsQ_2JIQ!%y-Q^ND3!(YPq7F{aGVIaL5TRUD0hKr;J|U?Z!(@6@Oun%Oa2?f+;l%{ zn49#MWnsa}^Bv)E{)Z&s3f>qIeIQH=!GWD7VH*x^vByO=A7@7Je8XH&SIb<#xzc!X zhkFEE$GO`gxaqEux#{yE-zY9EI9Y{+ve=n w$<^)IJ)OrKPDF6O2p$x{l@T1h>8p?M;VCww{kuP|&ffPcjrPaif2{O>0J^js!2kdN literal 65624 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 SJ6Cadj1i88Yy8fQKJI_yi2t?# diff --git a/lib/libmain.a b/lib/libmain.a new file mode 100644 index 0000000000000000000000000000000000000000..60657ec70a21b62084159e73422b8cb181e2e616 GIT binary patch literal 123992 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 literal 0 HcmV?d00001 diff --git a/lib/libphy.a b/lib/libphy.a new file mode 100644 index 0000000000000000000000000000000000000000..e2508e35e383a2eece6119007cb8946eaf40c9f7 GIT binary patch literal 184684 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

kdL+38QfL8B4PZh@g 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%0A5#of_Hf(>bDZo{uit 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­rzHLF6e!~AU4^5VqiHQlPsJ~8}DpULV! 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 KJl=7+d{%Mh_~bVyu}Vnf>XSLW0sh^pezCzN8Pb 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 M?$g3K*VNhje_%y^rT_o{ diff --git a/lib/libwpa.a b/lib/libwpa.a new file mode 100644 index 0000000000000000000000000000000000000000..a8fa39ab5a1fbae866726111cb7fda0fdcef9f92 GIT binary patch literal 160156 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 Nb;?%%-~24_e*kAnSJwal literal 0 HcmV?d00001 diff --git a/third_party/freertos/croutine.c b/third_party/freertos/croutine.c index e5a591c..8ee5311 100644 --- a/third_party/freertos/croutine.c +++ b/third_party/freertos/croutine.c @@ -238,12 +238,14 @@ static void prvCheckPendingReadyList( void ) corCRCB *pxUnblockedCRCB; /* The pending ready list can be accessed by an ISR. */ - portDISABLE_INTERRUPTS(); + //portDISABLE_INTERRUPTS(); +PortDisableInt_NoNest(); { pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) ); ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); } - portENABLE_INTERRUPTS(); +// portENABLE_INTERRUPTS(); +PortEnableInt_NoNest(); ( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); @@ -284,7 +286,8 @@ corCRCB *pxCRCB; break; } - portDISABLE_INTERRUPTS(); + //portDISABLE_INTERRUPTS(); +PortDisableInt_NoNest(); { /* The event could have occurred just before this critical section. If this is the case then the generic list item will @@ -299,7 +302,8 @@ corCRCB *pxCRCB; ( void ) uxListRemove( &( pxCRCB->xEventListItem ) ); } } - portENABLE_INTERRUPTS(); +// portENABLE_INTERRUPTS(); +PortEnableInt_NoNest(); prvAddCoRoutineToReadyQueue( pxCRCB ); } diff --git a/third_party/freertos/heap_4.c b/third_party/freertos/heap_4.c index 841db76..51eae94 100644 --- a/third_party/freertos/heap_4.c +++ b/third_party/freertos/heap_4.c @@ -156,7 +156,8 @@ void *pvReturn = NULL; // printf("%s %d %d\n", __func__, xWantedSize, xFreeBytesRemaining); - vTaskSuspendAll(); +// vTaskSuspendAll(); + ETS_INTR_LOCK(); { /* If this is the first call to malloc then the heap will require initialisation to setup the list of free blocks. */ @@ -239,7 +240,8 @@ void *pvReturn = NULL; } } } - xTaskResumeAll(); +// xTaskResumeAll(); + ETS_INTR_UNLOCK(); #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { @@ -287,13 +289,15 @@ xBlockLink *pxLink; allocated. */ pxLink->xBlockSize &= ~xBlockAllocatedBit; - vTaskSuspendAll(); +// vTaskSuspendAll(); + ETS_INTR_LOCK(); { /* Add this block to the list of free blocks. */ xFreeBytesRemaining += pxLink->xBlockSize; prvInsertBlockIntoFreeList( ( ( xBlockLink * ) pxLink ) ); } - xTaskResumeAll(); +// xTaskResumeAll(); + ETS_INTR_UNLOCK(); } } } diff --git a/third_party/freertos/list.c b/third_party/freertos/list.c index b2dba36..c6b23d0 100644 --- a/third_party/freertos/list.c +++ b/third_party/freertos/list.c @@ -100,7 +100,7 @@ vListInitialiseItem( xListItem * const pxItem ) } /*-----------------------------------------------------------*/ -void ICACHE_FLASH_ATTR +void vListInsertEnd( xList * const pxList, xListItem * const pxNewListItem ) { xListItem * pxIndex; diff --git a/third_party/freertos/port.c b/third_party/freertos/port.c index d9e4b10..fee9337 100644 --- a/third_party/freertos/port.c +++ b/third_party/freertos/port.c @@ -86,8 +86,8 @@ unsigned cpu_sr; variable. */ static unsigned portBASE_TYPE uxCriticalNesting = 0; -void ICACHE_FLASH_ATTR vPortEnterCritical( void ); -void ICACHE_FLASH_ATTR vPortExitCritical( void ); +void vPortEnterCritical( void ); +void vPortExitCritical( void ); /* * See header file for description. */ @@ -123,23 +123,23 @@ pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *p } - - - - void PendSV( char req ) { -char tmp=0; -if(ClosedLv1Isr == 0) -{ - vPortEnterCritical(); - tmp = 1; -} + char tmp=0; +//ETS_INTR_LOCK(); + + if( NMIIrqIsOn == 0 ) + { + vPortEnterCritical(); + //PortDisableInt_NoNest(); + tmp = 1; + } + if(req ==1) { SWReq = 1; } - else + else if(req ==2) HdlMacSig= 1; #if 0 GPIO_REG_WRITE(GPIO_STATUS_W1TS_ADDRESS, 0x40); @@ -188,20 +188,20 @@ void SoftIsrHdl(void) { //if(DbgVal5==1) //printf("GP_%d,",SWReq); + PendSvIsPosted = 0; portBASE_TYPE xHigherPriorityTaskWoken=pdFALSE ; if(HdlMacSig == 1) { - HdlMacSig = 0; xHigherPriorityTaskWoken = MacIsrSigPostDefHdl(); + HdlMacSig = 0; } if( xHigherPriorityTaskWoken || (SWReq==1)) { //if( DbgVal5==1 || DbgVal10==1 ) //printf("_x_s,"); - SWReq = 0; _xt_timer_int1(); + SWReq = 0; } - PendSvIsPosted = 0; } #endif @@ -244,6 +244,7 @@ xPortStartScheduler( void ) // REG_SET_BIT(0x3ff2006c, BIT(4)); /* Restore the context of the first task that is going to run. */ + XT_RTOS_INT_EXIT(); /* Should not get here as the tasks are now running! */ @@ -262,14 +263,13 @@ vPortEndScheduler( void ) /*-----------------------------------------------------------*/ static unsigned int tick_lock=0; -char ClosedLv1Isr = 0; +static char ClosedLv1Isr = 0; -void ICACHE_FLASH_ATTR -vPortEnterCritical( void ) +void vPortEnterCritical( void ) { if(NMIIrqIsOn == 0) { - if( uxCriticalNesting == 0 ) + //if( uxCriticalNesting == 0 ) { if( ClosedLv1Isr !=1 ) { @@ -283,8 +283,7 @@ vPortEnterCritical( void ) } /*-----------------------------------------------------------*/ -void ICACHE_FLASH_ATTR -vPortExitCritical( void ) +void vPortExitCritical( void ) { if(NMIIrqIsOn == 0) { @@ -302,10 +301,11 @@ vPortExitCritical( void ) } } -static unsigned int tick_lock1=0; -void ICACHE_FLASH_ATTR -vPortEnterCritical1( void ) + +void +PortDisableInt_NoNest( void ) { +//os_printf("ERRRRRRR\n"); if(NMIIrqIsOn == 0) { if( ClosedLv1Isr !=1 ) @@ -316,9 +316,10 @@ vPortEnterCritical1( void ) } } -void ICACHE_FLASH_ATTR -vPortExitCritical1( void ) +void +PortEnableInt_NoNest( void ) { +os_printf("ERRRRR\n"); if(NMIIrqIsOn == 0) { if( ClosedLv1Isr ==1 ) diff --git a/third_party/freertos/queue.c b/third_party/freertos/queue.c index 12bc008..649b757 100644 --- a/third_party/freertos/queue.c +++ b/third_party/freertos/queue.c @@ -930,7 +930,7 @@ xQUEUE * const pxQueue = ( xQUEUE * ) xQueue; #endif /* configUSE_ALTERNATIVE_API */ /*-----------------------------------------------------------*/ -signed portBASE_TYPE ICACHE_FLASH_ATTR +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle xQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) { signed portBASE_TYPE xReturn; @@ -1201,7 +1201,7 @@ xQUEUE * const pxQueue = ( xQUEUE * ) xQueue; } /*-----------------------------------------------------------*/ -signed portBASE_TYPE ICACHE_FLASH_ATTR +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle xQueue, const void * const pvBuffer, signed portBASE_TYPE *pxHigherPriorityTaskWoken ) { signed portBASE_TYPE xReturn; @@ -1279,7 +1279,7 @@ xQUEUE * const pxQueue = ( xQUEUE * ) xQueue; } /*-----------------------------------------------------------*/ -signed portBASE_TYPE ICACHE_FLASH_ATTR +signed portBASE_TYPE xQueuePeekFromISR( xQueueHandle xQueue, const void * const pvBuffer ) { signed portBASE_TYPE xReturn; @@ -1350,7 +1350,7 @@ unsigned portBASE_TYPE uxReturn; } /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ /*-----------------------------------------------------------*/ -unsigned portBASE_TYPE ICACHE_FLASH_ATTR +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle xQueue ) { unsigned portBASE_TYPE uxReturn; @@ -1414,7 +1414,7 @@ xQUEUE * const pxQueue = ( xQUEUE * ) xQueue; #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -static void ICACHE_FLASH_ATTR +static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition ) { if( pxQueue->uxItemSize == ( unsigned portBASE_TYPE ) 0 ) @@ -1465,7 +1465,7 @@ prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xP } /*-----------------------------------------------------------*/ -static void ICACHE_FLASH_ATTR +static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void * const pvBuffer ) { if( pxQueue->uxQueueType != queueQUEUE_IS_MUTEX ) @@ -1602,7 +1602,7 @@ signed portBASE_TYPE xReturn; } /*-----------------------------------------------------------*/ -signed portBASE_TYPE ICACHE_FLASH_ATTR +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle xQueue ) { signed portBASE_TYPE xReturn; @@ -1643,7 +1643,7 @@ signed portBASE_TYPE xReturn; } /*-----------------------------------------------------------*/ -signed portBASE_TYPE ICACHE_FLASH_ATTR +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle xQueue ) { signed portBASE_TYPE xReturn; @@ -1675,7 +1675,7 @@ signed portBASE_TYPE xReturn; between the check to see if the queue is full and blocking on the queue. */ //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); { if( prvIsQueueFull( pxQueue ) != pdFALSE ) { @@ -1687,22 +1687,22 @@ vPortEnterCritical1(); return indicating that we need to block. */ vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); return errQUEUE_BLOCKED; } else { //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); return errQUEUE_FULL; } } } //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); { if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) { @@ -1731,7 +1731,7 @@ vPortEnterCritical1(); } } //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); return xReturn; } @@ -1751,7 +1751,7 @@ vPortExitCritical1(); between the check to see if the queue is empty and blocking on the queue. */ //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); { if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ) { @@ -1763,22 +1763,22 @@ vPortEnterCritical1(); indicating that we need to block. */ vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); return errQUEUE_BLOCKED; } else { //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); return errQUEUE_FULL; } } } //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); { if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) { @@ -1812,7 +1812,7 @@ vPortEnterCritical1(); } } //portENABLE_INTERRUPTS(); -vPortExitCritical1(); +PortEnableInt_NoNest(); return xReturn; } @@ -1822,7 +1822,7 @@ vPortExitCritical1(); #if ( configUSE_CO_ROUTINES == 1 ) - signed portBASE_TYPE ICACHE_FLASH_ATTR + signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle xQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ) { xQUEUE * const pxQueue = ( xQUEUE * ) xQueue; @@ -1855,7 +1855,7 @@ vPortExitCritical1(); #if ( configUSE_CO_ROUTINES == 1 ) - signed portBASE_TYPE ICACHE_FLASH_ATTR + signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle xQueue, void *pvBuffer, signed portBASE_TYPE *pxCoRoutineWoken ) { signed portBASE_TYPE xReturn; @@ -2081,7 +2081,7 @@ vPortExitCritical1(); #if ( configUSE_QUEUE_SETS == 1 ) - xQueueSetMemberHandle ICACHE_FLASH_ATTR + xQueueSetMemberHandle xQueueSelectFromSetFromISR( xQueueSetHandle xQueueSet ) { xQueueSetMemberHandle xReturn = NULL; diff --git a/third_party/freertos/tasks.c b/third_party/freertos/tasks.c index 30ad7cf..808dbaa 100644 --- a/third_party/freertos/tasks.c +++ b/third_party/freertos/tasks.c @@ -101,7 +101,7 @@ privileged Vs unprivileged linkage and placement. */ /* * Defines the size, in words, of the stack allocated to the idle task. */ -#define tskIDLE_STACK_SIZE configMINIMAL_STACK_SIZE +#define tskIDLE_STACK_SIZE 176 //configMINIMAL_STACK_SIZE /* * Task control block. A task control block (TCB) is allocated for each task, @@ -1172,7 +1172,7 @@ tskTCB * pxNewTCB; #if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) - portBASE_TYPE ICACHE_FLASH_ATTR + portBASE_TYPE xTaskResumeFromISR( xTaskHandle xTaskToResume ) { portBASE_TYPE xYieldRequired = pdFALSE; @@ -1270,7 +1270,7 @@ printf("idle_task_hdl : %x\n", xIdleTaskHandle); STEPPING THROUGH HERE USING A DEBUGGER CAN CAUSE BIG PROBLEMS IF THE DEBUGGER ALLOWS INTERRUPTS TO BE PROCESSED. */ //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); xSchedulerRunning = pdTRUE; xTickCount = ( portTickType ) 0U; @@ -1310,7 +1310,7 @@ vTaskEndScheduler( void ) layer must ensure interrupts enable bit is left in the correct state. */ //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); xSchedulerRunning = pdFALSE; vPortEndScheduler(); } @@ -1441,7 +1441,7 @@ portTickType xTicks; } /*-----------------------------------------------------------*/ -portTickType ICACHE_FLASH_ATTR +portTickType xTaskGetTickCountFromISR( void ) { portTickType xReturn; @@ -1974,7 +1974,7 @@ portTickType xTimeToWake; #endif /* configUSE_TIMERS */ /*-----------------------------------------------------------*/ -signed portBASE_TYPE ICACHE_FLASH_ATTR +signed portBASE_TYPE xTaskRemoveFromEventList( const xList * const pxEventList ) { tskTCB *pxUnblockedTCB; @@ -2734,7 +2734,7 @@ tskTCB *pxNewTCB; #if ( configUSE_MUTEXES == 1 ) - void ICACHE_FLASH_ATTR + void vTaskPriorityDisinherit( xTaskHandle const pxMutexHolder ) { tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder; @@ -2769,7 +2769,7 @@ tskTCB *pxNewTCB; vTaskEnterCritical( void ) { //portDISABLE_INTERRUPTS(); -vPortEnterCritical1(); +PortDisableInt_NoNest(); if( xSchedulerRunning != pdFALSE ) { ( pxCurrentTCB->uxCriticalNesting )++; @@ -2793,7 +2793,7 @@ vPortEnterCritical1(); if( pxCurrentTCB->uxCriticalNesting == 0U ) { //portENABLE_INTERRUPTS(); -vPortExitCritical1(); + PortEnableInt_NoNest(); } } } diff --git a/third_party/lwip/FILES b/third_party/lwip/FILES deleted file mode 100644 index 10115a7..0000000 --- a/third_party/lwip/FILES +++ /dev/null @@ -1,13 +0,0 @@ -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/third_party/lwip/Makefile b/third_party/lwip/Makefile deleted file mode 100644 index 30db445..0000000 --- a/third_party/lwip/Makefile +++ /dev/null @@ -1,50 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR -UP_EXTRACT_DIR = .. -GEN_LIBS = liblwip.a -COMPONENTS_liblwip = api/liblwipapi.a \ - arch/liblwiparch.a \ - core/liblwipcore.a \ - core/ipv4/liblwipipv4.a \ - netif/liblwipnetif.a -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/third_party/lwip/api/Makefile b/third_party/lwip/api/Makefile deleted file mode 100644 index ef34edc..0000000 --- a/third_party/lwip/api/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = liblwipapi.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/third_party/lwip/api/api_lib.c b/third_party/lwip/api/api_lib.c deleted file mode 100644 index 9826cf3..0000000 --- a/third_party/lwip/api/api_lib.c +++ /dev/null @@ -1,788 +0,0 @@ -/** - * @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 - * - */ - -/* 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 - -/** - * 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* ICACHE_FLASH_ATTR -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) { - err_t err; - msg.msg.msg.n.proto = proto; - msg.msg.conn = conn; - TCPIP_APIMSG((&msg), lwip_netconn_do_newconn, err); - if (err != 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 ICACHE_FLASH_ATTR -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 = lwip_netconn_do_delconn; - msg.msg.conn = conn; - tcpip_apimsg(&msg); - - netconn_free(conn); - - /* don't care for return value of lwip_netconn_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 ICACHE_FLASH_ATTR -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.msg.conn = conn; - msg.msg.msg.ad.ipaddr = ip_2_ipX(addr); - msg.msg.msg.ad.port = port; - msg.msg.msg.ad.local = local; - TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err); - - 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 ICACHE_FLASH_ATTR -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.msg.conn = conn; - msg.msg.msg.bc.ipaddr = addr; - msg.msg.msg.bc.port = port; - TCPIP_APIMSG(&msg, lwip_netconn_do_bind, err); - - 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 ICACHE_FLASH_ATTR -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.msg.conn = conn; - msg.msg.msg.bc.ipaddr = addr; - msg.msg.msg.bc.port = port; -#if LWIP_TCP -#if (LWIP_UDP || LWIP_RAW) - if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) -#endif /* (LWIP_UDP || LWIP_RAW) */ - { - /* The TCP version waits for the connect to succeed, - so always needs to use message passing. */ - msg.function = lwip_netconn_do_connect; - err = tcpip_apimsg(&msg); - } -#endif /* LWIP_TCP */ -#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP - else -#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */ -#if (LWIP_UDP || LWIP_RAW) - { - /* UDP and RAW only set flags, so we can use core-locking. */ - TCPIP_APIMSG(&msg, lwip_netconn_do_connect, err); - } -#endif /* (LWIP_UDP || LWIP_RAW) */ - - 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 ICACHE_FLASH_ATTR -netconn_disconnect(struct netconn *conn) -{ - struct api_msg msg; - err_t err; - - LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); - - msg.msg.conn = conn; - TCPIP_APIMSG(&msg, lwip_netconn_do_disconnect, err); - - 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 ICACHE_FLASH_ATTR -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.msg.conn = conn; -#if TCP_LISTEN_BACKLOG - msg.msg.msg.lb.backlog = backlog; -#endif /* TCP_LISTEN_BACKLOG */ - TCPIP_APIMSG(&msg, lwip_netconn_do_listen, err); - - 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 ICACHE_FLASH_ATTR -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.msg.conn = conn; - /* don't care for the return value of lwip_netconn_do_recv */ - TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); -#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 ICACHE_FLASH_ATTR -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 (LWIP_UDP || LWIP_RAW) - if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) -#endif /* (LWIP_UDP || LWIP_RAW) */ - { - 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.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 lwip_netconn_do_recv */ - TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); - } - - /* 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 ICACHE_FLASH_ATTR -netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) -{ - LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && - NETCONNTYPE_GROUP(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 ICACHE_FLASH_ATTR -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 (LWIP_UDP || LWIP_RAW) - if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) -#endif /* (LWIP_UDP || LWIP_RAW) */ - { - 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; - ipX_addr_set_any(LWIP_IPV6, &buf->addr); - *new_buf = buf; - /* don't set conn->last_err: it's only ERR_OK, anyway */ - return ERR_OK; - } -#endif /* LWIP_TCP */ -#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) - else -#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ - { -#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 ICACHE_FLASH_ATTR -netconn_recved(struct netconn *conn, u32_t length) -{ -#if LWIP_TCP - if ((conn != NULL) && (NETCONNTYPE_GROUP(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.msg.conn = conn; - msg.msg.msg.r.len = length; - /* don't care for the return value of lwip_netconn_do_recv */ - TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); - } -#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 ICACHE_FLASH_ATTR -netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port) -{ - if (buf != NULL) { - ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &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 ICACHE_FLASH_ATTR -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.msg.conn = conn; - msg.msg.msg.b = buf; - TCPIP_APIMSG(&msg, lwip_netconn_do_send, err); - - 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 - * @param bytes_written pointer to a location that receives the number of written bytes - * @return ERR_OK if data was sent, any other err_t on error - */ -err_t ICACHE_FLASH_ATTR -netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, - u8_t apiflags, size_t *bytes_written) -{ - struct api_msg msg; - err_t err; - u8_t dontblock; - - LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); - LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;); - if (size == 0) { - return ERR_OK; - } - dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); - if (dontblock && !bytes_written) { - /* This implies netconn_write() cannot be used for non-blocking send, since - it has no way to return the number of bytes written. */ - return ERR_VAL; - } - - /* non-blocking write sends as much */ - msg.msg.conn = conn; - msg.msg.msg.w.dataptr = dataptr; - msg.msg.msg.w.apiflags = apiflags; - msg.msg.msg.w.len = size; -#if LWIP_SO_SNDTIMEO - if (conn->send_timeout != 0) { - /* get the time we started, which is later compared to - sys_now() + conn->send_timeout */ - msg.msg.msg.w.time_started = sys_now(); - } else { - msg.msg.msg.w.time_started = 0; - } -#endif /* LWIP_SO_SNDTIMEO */ - - /* 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. */ - TCPIP_APIMSG(&msg, lwip_netconn_do_write, err); - if ((err == ERR_OK) && (bytes_written != NULL)) { - if (dontblock -#if LWIP_SO_SNDTIMEO - || (conn->send_timeout != 0) -#endif /* LWIP_SO_SNDTIMEO */ - ) { - /* nonblocking write: maybe the data has been sent partly */ - *bytes_written = msg.msg.msg.w.len; - } else { - /* blocking call succeeded: all data has been sent if it */ - *bytes_written = size; - } - } - - 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 ICACHE_FLASH_ATTR -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 = lwip_netconn_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 lwip_netconn_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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 || (LWIP_IPV6 && LWIP_IPV6_MLD) -/** - * 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 ICACHE_FLASH_ATTR -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.msg.conn = conn; - msg.msg.msg.jl.multiaddr = ip_2_ipX(multiaddr); - msg.msg.msg.jl.netif_addr = ip_2_ipX(netif_addr); - msg.msg.msg.jl.join_or_leave = join_or_leave; - TCPIP_APIMSG(&msg, lwip_netconn_do_join_leave_group, err); - - NETCONN_SET_SAFE_ERR(conn, err); - return err; -} -#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ - -#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 ICACHE_FLASH_ATTR -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(lwip_netconn_do_gethostbyname, &msg); - sys_sem_wait(&sem); - sys_sem_free(&sem); - - return err; -} -#endif /* LWIP_DNS*/ - -#endif /* LWIP_NETCONN */ diff --git a/third_party/lwip/api/api_msg.c b/third_party/lwip/api/api_msg.c deleted file mode 100644 index 6b4d63e..0000000 --- a/third_party/lwip/api/api_msg.c +++ /dev/null @@ -1,1611 +0,0 @@ -/** - * @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 - * - */ - -#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 "lwip/mld6.h" - -#include - -#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 lwip_netconn_do_writemore(struct netconn *conn); -static void lwip_netconn_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 ICACHE_FLASH_ATTR -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; - ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_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 ICACHE_FLASH_ATTR -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; - ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr); - buf->port = port; -#if LWIP_NETBUF_RECVINFO - { - /* get the UDP header - always in the first pbuf, ensured by udp_input */ - const struct udp_hdr* udphdr = ipX_next_header_ptr(); -#if LWIP_CHECKSUM_ON_COPY - buf->flags = NETBUF_FLAG_DESTADDR; -#endif /* LWIP_CHECKSUM_ON_COPY */ - ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_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 ICACHE_FLASH_ATTR -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; - - if (conn == NULL) { - return ERR_VAL; - } - LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); - - 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 ICACHE_FLASH_ATTR -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) { - lwip_netconn_do_writemore(conn); - } else if (conn->state == NETCONN_CLOSE) { - lwip_netconn_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 ICACHE_FLASH_ATTR -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) { - lwip_netconn_do_writemore(conn); - } else if (conn->state == NETCONN_CLOSE) { - lwip_netconn_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 ICACHE_FLASH_ATTR -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 lwip_netconn_do_writemore/lwip_netconn_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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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! */ - /* remove all references to this netconn from the pcb */ - struct tcp_pcb* pcb = newconn->pcb.tcp; - tcp_arg(pcb, NULL); - tcp_recv(pcb, NULL); - tcp_sent(pcb, NULL); - tcp_poll(pcb, NULL, 4); - tcp_err(pcb, NULL); - /* remove reference from to the pcb from this netconn */ - 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 lwip_netconn_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 ICACHE_FLASH_ATTR -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) { - 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) { -#if LWIP_UDPLITE - if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) { - udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); - } -#endif /* LWIP_UDPLITE */ - if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) { - 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) { - setup_tcp(msg->conn); - } - break; -#endif /* LWIP_TCP */ - default: - /* Unsupported netconn type, e.g. protocol disabled */ - msg->err = ERR_VAL; - return; - } - if (msg->conn->pcb.ip == NULL) { - msg->err = ERR_MEM; - } -#if LWIP_IPV6 - else { - if (NETCONNTYPE_ISIPV6(msg->conn->type)) { - ip_set_v6(msg->conn->pcb.ip, 1); - } - } -#endif /* LWIP_IPV6 */ -} - -/** - * 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 ICACHE_FLASH_ATTR -lwip_netconn_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* ICACHE_FLASH_ATTR -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 all sizes are the same, every compiler should optimize this switch to nothing, */ - 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); - goto free_and_return; - } - - if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { - goto free_and_return; - } - if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { - sys_sem_free(&conn->op_completed); - goto free_and_return; - } - -#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_SNDTIMEO - conn->send_timeout = 0; -#endif /* LWIP_SO_SNDTIMEO */ -#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; -free_and_return: - memp_free(MEMP_NETCONN, conn); - return NULL; -} - -/** - * 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 (NETCONNTYPE_GROUP(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 ICACHE_FLASH_ATTR -lwip_netconn_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", (NETCONNTYPE_GROUP(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 (close) { - err = tcp_close(conn->pcb.tcp); - } else { - err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx); - } - if (err == ERR_OK) { - /* Closing succeeded */ - conn->current_msg->err = ERR_OK; - conn->current_msg = NULL; - conn->state = NETCONN_NONE; - if (close) { - /* 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! */ - 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 ICACHE_FLASH_ATTR -lwip_netconn_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("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", - NETCONNTYPE_GROUP(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; - lwip_netconn_do_close_internal(msg->conn); - /* API_EVENT is called inside lwip_netconn_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 ICACHE_FLASH_ATTR -lwip_netconn_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/lwip_netconn_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 ICACHE_FLASH_ATTR -lwip_netconn_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 ((NETCONNTYPE_GROUP(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 ICACHE_FLASH_ATTR -lwip_netconn_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; - if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { - /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ - sys_sem_signal(&msg->conn->op_completed); - return; - } - } 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, lwip_netconn_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 lwip_netconn_do_connected (or err_tcp()), - * when the connection is established! */ - return; - } - } - } - /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ - sys_sem_signal(&msg->conn->op_completed); - return; -#endif /* LWIP_TCP */ - default: - LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); - break; - } - } - /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(), - so use TCPIP_APIMSG_ACK() here. */ - TCPIP_APIMSG_ACK(msg); -} - -/** - * 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 ICACHE_FLASH_ATTR -lwip_netconn_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 ICACHE_FLASH_ATTR -lwip_netconn_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 (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { - if (msg->conn->state == NETCONN_NONE) { - struct tcp_pcb* lpcb; -#if LWIP_IPV6 - if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) { -#if TCP_LISTEN_BACKLOG - lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); -#else /* TCP_LISTEN_BACKLOG */ - lpcb = tcp_listen_dual(msg->conn->pcb.tcp); -#endif /* TCP_LISTEN_BACKLOG */ - } else -#endif /* LWIP_IPV6 */ - { -#if TCP_LISTEN_BACKLOG - lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); -#else /* TCP_LISTEN_BACKLOG */ - 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; - } - } - } - } else { - msg->err = ERR_ARG; - } - } - } - 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 ICACHE_FLASH_ATTR -lwip_netconn_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 (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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, ipX_2_ip(&msg->msg.b->addr)); - } - break; -#endif -#if LWIP_UDP - case NETCONN_UDP: -#if LWIP_CHECKSUM_ON_COPY - if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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, - ipX_2_ip(&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 (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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, ipX_2_ip(&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 ICACHE_FLASH_ATTR -lwip_netconn_do_recv(struct api_msg_msg *msg) -{ - msg->err = ERR_OK; - if (msg->conn->pcb.tcp != NULL) { - if (NETCONNTYPE_GROUP(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 lwip_netconn_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 ICACHE_FLASH_ATTR -lwip_netconn_do_writemore(struct netconn *conn) -{ - err_t err; - 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); - -#if LWIP_SO_SNDTIMEO - if ((conn->send_timeout != 0) && - ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { - write_finished = 1; - if (conn->write_offset == 0) { - /* nothing has been written */ - err = ERR_WOULDBLOCK; - conn->current_msg->msg.w.len = 0; - } else { - /* partial write */ - err = ERR_OK; - conn->current_msg->msg.w.len = conn->write_offset; - } - } else -#endif /* LWIP_SO_SNDTIMEO */ - { - 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 (dontblock){ - if (!len) { - err = ERR_WOULDBLOCK; - goto err_mem; - } - } else { -#if LWIP_TCPIP_CORE_LOCKING - conn->flags |= NETCONN_FLAG_WRITE_DELAYED; -#endif - apiflags |= TCP_WRITE_FLAG_MORE; - } - } - LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); - err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); - /* if OK or memory error, check available space */ - if ((err == ERR_OK) || (err == ERR_MEM)) { -err_mem: - if (dontblock && (len < conn->current_msg->msg.w.len)) { - /* non-blocking write did not write everything: mark the pcb non-writable - and let poll_tcp check writable space to mark the pcb writable again */ - API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); - conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; - } else if ((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) || dontblock) { - /* return sent length */ - conn->current_msg->msg.w.len = conn->write_offset; - /* everything was written */ - write_finished = 1; - conn->write_offset = 0; - } - tcp_output(conn->pcb.tcp); - } else if ((err == ERR_MEM) && !dontblock) { - /* 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; - conn->current_msg->msg.w.len = 0; - } - } - 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 ICACHE_FLASH_ATTR -lwip_netconn_do_write(struct api_msg_msg *msg) -{ - if (ERR_IS_FATAL(msg->conn->last_err)) { - msg->err = msg->conn->last_err; - } else { - if (NETCONNTYPE_GROUP(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 lwip_netconn_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 (lwip_netconn_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 */ - lwip_netconn_do_writemore(msg->conn); -#endif /* LWIP_TCPIP_CORE_LOCKING */ - /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG - since lwip_netconn_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 ICACHE_FLASH_ATTR -lwip_netconn_do_getaddr(struct api_msg_msg *msg) -{ - if (msg->conn->pcb.ip != NULL) { - if (msg->msg.ad.local) { - ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), - msg->conn->pcb.ip->local_ip); - } else { - ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), - 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 ICACHE_FLASH_ATTR -lwip_netconn_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("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", - NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); - msg->err = ERR_INPROGRESS; - } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(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; - lwip_netconn_do_close_internal(msg->conn); - /* for tcp netconns, lwip_netconn_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 || (LWIP_IPV6 && LWIP_IPV6_MLD) -/** - * Join multicast groups for UDP netconns. - * Called from netconn_join_leave_group - * - * @param msg the api_msg_msg pointing to the connection - */ -void ICACHE_FLASH_ATTR -lwip_netconn_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 LWIP_IPV6 && LWIP_IPV6_MLD - if (PCB_ISIPV6(msg->conn->pcb.udp)) { - if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { - msg->err = mld6_joingroup(ipX_2_ip6(msg->msg.jl.netif_addr), - ipX_2_ip6(msg->msg.jl.multiaddr)); - } else { - msg->err = mld6_leavegroup(ipX_2_ip6(msg->msg.jl.netif_addr), - ipX_2_ip6(msg->msg.jl.multiaddr)); - } - } - else -#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ - { -#if LWIP_IGMP - if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { - msg->err = igmp_joingroup(ipX_2_ip(msg->msg.jl.netif_addr), - ipX_2_ip(msg->msg.jl.multiaddr)); - } else { - msg->err = igmp_leavegroup(ipX_2_ip(msg->msg.jl.netif_addr), - ipX_2_ip(msg->msg.jl.multiaddr)); - } -#endif /* LWIP_IGMP */ - } -#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 || (LWIP_IPV6 && LWIP_IPV6_MLD) */ - -#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 ICACHE_FLASH_ATTR -lwip_netconn_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 ICACHE_FLASH_ATTR -lwip_netconn_do_gethostbyname(void *arg) -{ - struct dns_api_msg *msg = (struct dns_api_msg*)arg; - - *msg->err = dns_gethostbyname(msg->name, msg->addr, lwip_netconn_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/third_party/lwip/api/err.c b/third_party/lwip/api/err.c deleted file mode 100644 index 7c5d94c..0000000 --- a/third_party/lwip/api/err.c +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @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 - * - */ - -#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/third_party/lwip/api/netbuf.c b/third_party/lwip/api/netbuf.c deleted file mode 100644 index cbd2fcc..0000000 --- a/third_party/lwip/api/netbuf.c +++ /dev/null @@ -1,245 +0,0 @@ -/** - * @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 - * - */ - -#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 - -/** - * 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 ICACHE_FLASH_ATTR -netbuf *netbuf_new(void) -{ - struct netbuf *buf; - - buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); - if (buf != NULL) { - buf->p = NULL; - buf->ptr = NULL; - ipX_addr_set_any(LWIP_IPV6, &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 - ipX_addr_set_any(LWIP_IPV6, &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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -netbuf_first(struct netbuf *buf) -{ - LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); - buf->ptr = buf->p; -} - -#endif /* LWIP_NETCONN */ diff --git a/third_party/lwip/api/netdb.c b/third_party/lwip/api/netdb.c deleted file mode 100644 index 324e049..0000000 --- a/third_party/lwip/api/netdb.c +++ /dev/null @@ -1,353 +0,0 @@ -/** - * @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 -#include - -/** helper struct for gethostbyname_r to access the char* buffer */ -struct gethostbyname_r_helper { - ip_addr_t *addr_list[2]; - 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* ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 == NULL)) { - /* 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 = HOST_NOT_FOUND; - return -1; - } - - /* copy the hostname into buf */ - MEMCPY(hostname, name, namelen); - hostname[namelen] = 0; - - /* fill hostent */ - h->addr_list[0] = &h->addr; - h->addr_list[1] = NULL; - h->aliases = NULL; - ret->h_name = hostname; - ret->h_aliases = &h->aliases; - ret->h_addrtype = AF_INET; - ret->h_length = sizeof(ip_addr_t); - ret->h_addr_list = (char**)&h->addr_list; - - /* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/api/netifapi.c b/third_party/lwip/api/netifapi.c deleted file mode 100644 index fc96459..0000000 --- a/third_party/lwip/api/netifapi.c +++ /dev/null @@ -1,160 +0,0 @@ -/** - * @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. - */ -static void ICACHE_FLASH_ATTR -netifapi_do_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. - */ -static void ICACHE_FLASH_ATTR -netifapi_do_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. - */ -static void ICACHE_FLASH_ATTR -netifapi_do_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 ICACHE_FLASH_ATTR -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 = netifapi_do_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 ICACHE_FLASH_ATTR -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 = netifapi_do_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 ICACHE_FLASH_ATTR -netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, - netifapi_errt_fn errtfunc) -{ - struct netifapi_msg msg; - msg.function = netifapi_do_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/third_party/lwip/api/sockets.c b/third_party/lwip/api/sockets.c deleted file mode 100644 index 5e6d767..0000000 --- a/third_party/lwip/api/sockets.c +++ /dev/null @@ -1,2568 +0,0 @@ -/** - * @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 - * - * Improved by Marc Boucher and David Haas - * - */ - -#include "lwip/opt.h" - -#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/api.h" -#include "lwip/sockets.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 - -#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \ - (sin)->sin_len = sizeof(struct sockaddr_in); \ - (sin)->sin_family = AF_INET; \ - (sin)->sin_port = htons((port)); \ - inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \ - memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0) -#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \ - inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \ - (port) = ntohs((sin)->sin_port); }while(0) - -#if LWIP_IPV6 -#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \ - ((namelen) == sizeof(struct sockaddr_in6))) -#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \ - ((name)->sa_family == AF_INET6)) -#define SOCK_ADDR_TYPE_MATCH(name, sock) \ - ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ - (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) -#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \ - (sin6)->sin6_len = sizeof(struct sockaddr_in6); \ - (sin6)->sin6_family = AF_INET6; \ - (sin6)->sin6_port = htons((port)); \ - (sin6)->sin6_flowinfo = 0; \ - inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0) -#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \ - if (isipv6) { \ - IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ - } else { \ - IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ - } } while(0) -#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \ - inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \ - (port) = ntohs((sin6)->sin6_port); }while(0) -#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \ - if (isipv6) { \ - SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ - } else { \ - SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ - } } while(0) -#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \ - (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6)) -#else /* LWIP_IPV6 */ -#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in)) -#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET) -#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 -#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \ - IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) -#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \ - SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) -#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) -#endif /* LWIP_IPV6 */ - -#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \ - IS_SOCK_ADDR_TYPE_VALID(name)) -#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \ - SOCK_ADDR_TYPE_MATCH(name, sock)) -#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) - - - -#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; -}; - -/** A struct sockaddr replacement that has the same alignment as sockaddr_in/ - * sockaddr_in6 if instantiated. - */ -union sockaddr_aligned { - struct sockaddr sa; -#if LWIP_IPV6 - struct sockaddr_in6 sin6; -#endif /* LWIP_IPV6 */ - struct sockaddr_in sin; -}; - - -/** 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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 = (NETCONNTYPE_GROUP(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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) -{ - struct lwip_sock *sock, *nsock; - struct netconn *newconn; - ipX_addr_t naddr; - u16_t port = 0; - int newsock; - 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)); - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { - sock_set_errno(sock, EOPNOTSUPP); - return EOPNOTSUPP; - } - 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); - - /* Note that POSIX only requires us to check addr is non-NULL. addrlen must - * not be NULL if addr is valid. - */ - if (addr != NULL) { - union sockaddr_aligned tempaddr; - /* get the IP address and port of the remote host */ - err = netconn_peer(newconn, ipX_2_ip(&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; - } - LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); - - IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port); - if (*addrlen > tempaddr.sa.sa_len) { - *addrlen = tempaddr.sa.sa_len; - } - MEMCPY(addr, &tempaddr, *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", s, newsock)); - if (addr != NULL) { - LWIP_DEBUGF(SOCKETS_DEBUG, (" addr=")); - ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); - } - - sock_set_errno(sock, 0); - return newsock; -} - -int ICACHE_FLASH_ATTR -lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) -{ - struct lwip_sock *sock; - ipX_addr_t local_addr; - u16_t local_port; - err_t err; - - sock = get_socket(s); - if (!sock) { - return -1; - } - - if (!SOCK_ADDR_TYPE_MATCH(name, sock)) { - /* sockaddr does not match socket type (IPv4/IPv6) */ - sock_set_errno(sock, err_to_errno(ERR_VAL)); - return -1; - } - - /* check size, familiy and alignment of 'name' */ - LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) && - IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)), - sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - LWIP_UNUSED_ARG(namelen); - - SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); - ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port)); - - err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), 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 ICACHE_FLASH_ATTR -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 = NETCONNTYPE_GROUP(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 ICACHE_FLASH_ATTR -lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) -{ - struct lwip_sock *sock; - err_t err; - - sock = get_socket(s); - if (!sock) { - return -1; - } - - if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { - /* sockaddr does not match socket type (IPv4/IPv6) */ - sock_set_errno(sock, err_to_errno(ERR_VAL)); - return -1; - } - - /* check size, familiy and alignment of 'name' */ - LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) && - IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name), - sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - LWIP_UNUSED_ARG(namelen); - if (name->sa_family == AF_UNSPEC) { - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); - err = netconn_disconnect(sock->conn); - } else { - ipX_addr_t remote_addr; - u16_t remote_port; - SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); - ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port)); - - err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), 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 ICACHE_FLASH_ATTR -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)); - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { - sock_set_errno(sock, EOPNOTSUPP); - return EOPNOTSUPP; - } - sock_set_errno(sock, err_to_errno(err)); - return -1; - } - - sock_set_errno(sock, 0); - return 0; -} - -int ICACHE_FLASH_ATTR -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; - 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 (NETCONNTYPE_GROUP(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 (NETCONNTYPE_GROUP(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 (NETCONNTYPE_GROUP(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) { -//#if !SOCKETS_DEBUG - if (from && fromlen) -//#endif /* !SOCKETS_DEBUG */ - { - u16_t port; - ipX_addr_t tmpaddr; - ipX_addr_t *fromaddr; - union sockaddr_aligned saddr; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { - fromaddr = &tmpaddr; - /* @todo: this does not work for IPv6, yet */ - netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0); - } else { - port = netbuf_fromport((struct netbuf *)buf); - fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf); - } - IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), - &saddr, fromaddr, port); - ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), - SOCKETS_DEBUG, fromaddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); -//#if SOCKETS_DEBUG - if (from && fromlen) -//#endif /* SOCKETS_DEBUG */ - { - if (*fromlen > saddr.sa.sa_len) { - *fromlen = saddr.sa.sa_len; - } - MEMCPY(from, &saddr, *fromlen); - } else { - /*fix the code for setting the UDP PROTO's remote infomation by liuh at 2014.8.27*/ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP){ - sock->conn->pcb.udp->remote_ip.addr = fromaddr->addr; - sock->conn->pcb.udp->remote_port = port; - } - } - } - } - - /* 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 ((NETCONNTYPE_GROUP(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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { - pbuf_free((struct pbuf *)buf); - } else { - netbuf_delete((struct netbuf *)buf); - } - } - } - } while (!done); - - if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) { - /* update receive window */ - netconn_recved(sock->conn, (u32_t)off); - } - sock_set_errno(sock, 0); - return off; -} - -int ICACHE_FLASH_ATTR -lwip_read(int s, void *mem, size_t len) -{ - return lwip_recvfrom(s, mem, len, 0, NULL, NULL); -} - -int ICACHE_FLASH_ATTR -lwip_recv(int s, void *mem, size_t len, int flags) -{ - return lwip_recvfrom(s, mem, len, flags, NULL, NULL); -} - -int ICACHE_FLASH_ATTR -lwip_send(int s, const void *data, size_t size, int flags) -{ - struct lwip_sock *sock; - err_t err; - u8_t write_flags; - size_t written; - - 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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) */ - } - - write_flags = NETCONN_COPY | - ((flags & MSG_MORE) ? NETCONN_MORE : 0) | - ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); - written = 0; - err = netconn_write_partly(sock->conn, data, size, write_flags, &written); - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); - sock_set_errno(sock, err_to_errno(err)); - return (err == ERR_OK ? (int)written : -1); -} - -int ICACHE_FLASH_ATTR -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; - u16_t remote_port; -#if !LWIP_TCPIP_CORE_LOCKING - struct netbuf buf; -#endif - - sock = get_socket(s); - if (!sock) { - return -1; - } - - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == 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 */ - } - - if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) { - /* sockaddr does not match socket type (IPv4/IPv6) */ - sock_set_errno(sock, err_to_errno(ERR_VAL)); - return -1; - } - - /* @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)) || - (IS_SOCK_ADDR_LEN_VALID(tolen) && - IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))), - sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); - LWIP_UNUSED_ARG(tolen); - -#if LWIP_TCPIP_CORE_LOCKING - /* Special speedup for fast UDP/RAW sending: call the raw API directly - instead of using the netconn functions. */ - { - struct pbuf* p; - ipX_addr_t *remote_addr; - ipX_addr_t remote_addr_tmp; - -#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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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 != NULL) { - SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6, - to, &remote_addr_tmp, remote_port); - remote_addr = &remote_addr_tmp; - } else { - remote_addr = &sock->conn->pcb.ip->remote_ip; -#if LWIP_UDP - if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { - remote_port = sock->conn->pcb.udp->remote_port; - } else -#endif /* LWIP_UDP */ - { - remote_port = 0; - } - } - - LOCK_TCPIP_CORE(); - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) { -#if LWIP_RAW - err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr)); -#else /* LWIP_RAW */ - err = ERR_ARG; -#endif /* LWIP_RAW */ - } -#if LWIP_UDP && LWIP_RAW - else -#endif /* LWIP_UDP && LWIP_RAW */ - { -#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, - ipX_2_ip(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, - ipX_2_ip(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) { - SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port); - } else { - /*fix the code for getting the UDP proto's remote information by liuh at 2014.8.27*/ -// ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP){ - buf.addr.addr = sock->conn->pcb.udp->remote_ip.addr; - remote_port = sock->conn->pcb.udp->remote_port; - } else { - remote_port = 0; - ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); - } - } - netbuf_fromport(&buf) = remote_port; - - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=", - s, data, short_size, flags)); - ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), - 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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 ICACHE_FLASH_ATTR -lwip_socket(int domain, int type, int protocol) -{ - struct netconn *conn; - int i; - -#if !LWIP_IPV6 - LWIP_UNUSED_ARG(domain); /* @todo: check this */ -#endif /* LWIP_IPV6 */ - - /* create a netconn */ - switch (type) { - case SOCK_RAW: - conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, 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(DOMAIN_TO_NETCONN_TYPE(domain, - ((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(DOMAIN_TO_NETCONN_TYPE(domain, 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 (NETCONNTYPE_GROUP(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 ICACHE_FLASH_ATTR -lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) -{ - struct lwip_sock *sock; - union sockaddr_aligned saddr; - ipX_addr_t naddr; - u16_t port; - - sock = get_socket(s); - if (!sock) { - return -1; - } - - /* get the IP address and port */ - /* @todo: this does not work for IPv6, yet */ - netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local); - IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), - &saddr, &naddr, port); - - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); - ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), - SOCKETS_DEBUG, &naddr); - LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port)); - - if (*namelen > saddr.sa.sa_len) { - *namelen = saddr.sa.sa_len; - } - MEMCPY(name, &saddr, *namelen); - - sock_set_errno(sock, 0); - return 0; -} - -int ICACHE_FLASH_ATTR -lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) -{ - return lwip_getaddrname(s, name, namelen, 0); -} - -int ICACHE_FLASH_ATTR -lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) -{ - return lwip_getaddrname(s, name, namelen, 1); -} - -int ICACHE_FLASH_ATTR -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: */ -#if LWIP_SO_SNDTIMEO - case SO_SNDTIMEO: -#endif /* LWIP_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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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(netconn_type(sock->conn)) != 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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_IPV6 -/* Level: IPPROTO_IPV6 */ - case IPPROTO_IPV6: - switch (optname) { - case IPV6_V6ONLY: - if (*optlen < sizeof(int)) { - err = EINVAL; - } - /* @todo: this does not work for datagram sockets, yet */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) - return 0; - break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_IPV6 */ - -#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 (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { - 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 ICACHE_FLASH_ATTR -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 = ip_get_option(sock->conn->pcb.ip, 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(netconn_type(sock->conn))) { - 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 = netconn_type(sock->conn); - LWIP_DEBUGF(SOCKETS_DEBUG, - ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", - s, *(int *)optval)); - } /* switch (netconn_type(sock->conn)) */ - 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_SNDTIMEO - case SO_SNDTIMEO: - *(int *)optval = netconn_get_sendtimeout(sock->conn); - break; -#endif /* LWIP_SO_SNDTIMEO */ -#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_IPV6 -/* Level: IPPROTO_IPV6 */ - case IPPROTO_IPV6: - switch (optname) { - case IPV6_V6ONLY: - *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", - s, *(int *)optval)); - break; - default: - LWIP_ASSERT("unhandled optname", 0); - break; - } /* switch (optname) */ - break; -#endif /* LWIP_IPV6 */ - -#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 ICACHE_FLASH_ATTR -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: */ -#if LWIP_SO_SNDTIMEO - case SO_SNDTIMEO: -#endif /* LWIP_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 ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; - case IP_MULTICAST_IF: - if (optlen < sizeof(struct in_addr)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; - case IP_MULTICAST_LOOP: - if (optlen < sizeof(u8_t)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; - case IP_ADD_MEMBERSHIP: - case IP_DROP_MEMBERSHIP: - if (optlen < sizeof(struct ip_mreq)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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_IPV6 -/* Level: IPPROTO_IPV6 */ - case IPPROTO_IPV6: - switch (optname) { - case IPV6_V6ONLY: - if (optlen < sizeof(int)) { - err = EINVAL; - } - - /* @todo: this does not work for datagram sockets, yet */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) - return 0; - - break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_IPV6 */ - -#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 (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) - 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 ICACHE_FLASH_ATTR -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) { - ip_set_option(sock->conn->pcb.ip, optname); - } else { - ip_reset_option(sock->conn->pcb.ip, 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_SNDTIMEO - case SO_SNDTIMEO: - netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval); - break; -#endif /* LWIP_SO_SNDTIMEO */ -#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_IPV6 -/* Level: IPPROTO_IPV6 */ - case IPPROTO_IPV6: - switch (optname) { - case IPV6_V6ONLY: - if (*(int*)optval) { - sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY; - } else { - sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY; - } - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n", - s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0))); - break; - default: - LWIP_ASSERT("unhandled optname", 0); - break; - } /* switch (optname) */ - break; -#endif /* LWIP_IPV6 */ - -#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 ICACHE_FLASH_ATTR -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 || LWIP_FIONREAD_LINUXMODE - case FIONREAD: - if (!argp) { - sock_set_errno(sock, EINVAL); - return -1; - } -#if LWIP_FIONREAD_LINUXMODE - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { - struct pbuf *p; - if (sock->lastdata) { - p = ((struct netbuf *)sock->lastdata)->p; - } else { - struct netbuf *rxbuf; - err_t err; - if (sock->rcvevent <= 0) { - *((u16_t*)argp) = 0; - } else { - err = netconn_recv(sock->conn, &rxbuf); - if (err != ERR_OK) { - *((u16_t*)argp) = 0; - } else { - sock->lastdata = rxbuf; - *((u16_t*)argp) = rxbuf->p->tot_len; - } - } - } - return 0; - } -#endif /* LWIP_FIONREAD_LINUXMODE */ - -#if LWIP_SO_RCVBUF - /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */ - 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 (NETCONNTYPE_GROUP(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; -#else /* LWIP_SO_RCVBUF */ - break; -#endif /* LWIP_SO_RCVBUF */ -#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */ - - 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: - break; - } /* switch (cmd) */ - 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; -} - -/** A minimal implementation of fcntl. - * Currently only the commands F_GETFL and F_SETFL are implemented. - * Only the flag O_NONBLOCK is implemented. - */ -int ICACHE_FLASH_ATTR -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/third_party/lwip/api/tcpip.c b/third_party/lwip/api/tcpip.c deleted file mode 100644 index 478f0e9..0000000 --- a/third_party/lwip/api/tcpip.c +++ /dev/null @@ -1,507 +0,0 @@ -/** - * @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 - * - */ - -#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; -sys_thread_t xLwipTaskHandle; - -#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 ICACHE_FLASH_ATTR -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 */ -#if LWIP_IPV6 - if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) { - ip6_input(msg->msg.inp.p, msg->msg.inp.netif); - } else -#endif /* LWIP_IPV6 */ - { - 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 */ - -#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 */ - - 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; - - case TCPIP_MSG_CALLBACK_STATIC: - LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); - msg->msg.cb.function(msg->msg.cb.ctx); - break; - - 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 ICACHE_FLASH_ATTR -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)) { - return ERR_VAL; - } - 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; -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 - */ -static sys_mutex_t ApiMsgMutex = NULL; - -err_t ICACHE_FLASH_ATTR -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_mutex_lock(&ApiMsgMutex); - - sys_mbox_post(&mbox, &msg); - sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0); - -sys_mutex_unlock(&ApiMsgMutex); - - return apimsg->msg.err; - } - return ERR_VAL; -} - -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ - -/** - * Allocate a structure for a static callback message and initialize it. - * This is intended to be used to send "static" messages from interrupt context. - * - * @param function the function to call - * @param ctx parameter passed to function - * @return a struct pointer to pass to tcpip_trycallback(). - */ -struct tcpip_callback_msg* ICACHE_FLASH_ATTR tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) -{ - struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); - if (msg == NULL) { - return NULL; - } - msg->type = TCPIP_MSG_CALLBACK_STATIC; - msg->msg.cb.function = function; - msg->msg.cb.ctx = ctx; - return (struct tcpip_callback_msg*)msg; -} - -/** - * Free a callback message allocated by tcpip_callbackmsg_new(). - * - * @param msg the message to free - */ -void ICACHE_FLASH_ATTR tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) -{ - memp_free(MEMP_TCPIP_MSG_API, msg); -} - -/** - * Try to post a callback-message to the tcpip_thread mbox - * This is intended to be used to send "static" messages from interrupt context. - * - * @param msg pointer to the message to post - * @return sys_mbox_trypost() return code - */ -err_t ICACHE_FLASH_ATTR -tcpip_trycallback(struct tcpip_callback_msg* msg) -{ - if (!sys_mbox_valid(&mbox)) { - return ERR_VAL; - } - return sys_mbox_trypost(&mbox, msg); -} - -/** - * 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 ICACHE_FLASH_ATTR -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_mutex_new(&ApiMsgMutex); -if(ApiMsgMutex==NULL) - printf("ApiMsgMutex create fail\n"); -else - printf("ApiMsgMutex created\n"); - - xLwipTaskHandle = 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -mem_free_callback(void *m) -{ - return tcpip_callback_with_block(mem_free, m, 0); -} - -#endif /* !NO_SYS */ diff --git a/third_party/lwip/arch/Makefile b/third_party/lwip/arch/Makefile deleted file mode 100644 index 5d7af3a..0000000 --- a/third_party/lwip/arch/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = liblwiparch.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/third_party/lwip/arch/sys_arch.c b/third_party/lwip/arch/sys_arch.c deleted file mode 100644 index 0363648..0000000 --- a/third_party/lwip/arch/sys_arch.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (c) 2001-2003 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 - * - */ - -/* lwIP includes. */ - -#include -#include "lwip/debug.h" -#include "lwip/def.h" -#include "lwip/sys.h" -#include "lwip/mem.h" -#include "arch/sys_arch.h" - - -/* Message queue constants. */ -#define archMESG_QUEUE_LENGTH (100)//( 6 ) -#define archPOST_BLOCK_TIME_MS ( ( unsigned portLONG ) 10000 ) - - -struct timeoutlist { - //struct sys_timeouts timeouts; - xTaskHandle pid; -}; - -/* This is the number of threads that can be started with sys_thread_new() */ -#define SYS_THREAD_MAX 4 - -//static struct timeoutlist timeoutlist[SYS_THREAD_MAX]; -//static u16_t nextthread = 0; -int intlevel = 0; - -/*-----------------------------------------------------------------------------------*/ -// Initialize sys arch -void ICACHE_FLASH_ATTR -sys_init(void) -{ -} - -/*-----------------------------------------------------------------------------------*/ -// Creates and returns a new semaphore. The "count" argument specifies -// the initial state of the semaphore. TBD finish and test -err_t ICACHE_FLASH_ATTR -sys_sem_new(sys_sem_t *sem, u8_t count) -{ - err_t xReturn = ERR_MEM; - vSemaphoreCreateBinary(*sem); - - if ((*sem) != NULL) { - if (count == 0) { // Means it can't be taken - xSemaphoreTake(*sem, 1); - } - - xReturn = ERR_OK; - } else { - ; // TBD need assert - } - - return xReturn; -} - - -/*-----------------------------------------------------------------------------------*/ -// Deallocates a semaphore -void ICACHE_FLASH_ATTR -sys_sem_free(sys_sem_t *sem) -{ - //vQueueDelete( sem ); - vSemaphoreDelete(*sem); -} - - -/*-----------------------------------------------------------------------------------*/ -// Signals a semaphore -void ICACHE_FLASH_ATTR -sys_sem_signal(sys_sem_t *sem) -{ - xSemaphoreGive(*sem); -} - - -/*-----------------------------------------------------------------------------------*/ -/* - 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 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. -*/ -u32_t ICACHE_FLASH_ATTR -sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) -{ - portTickType StartTime, EndTime, Elapsed; - unsigned long ulReturn; - - StartTime = xTaskGetTickCount(); - - if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { - EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; - - if (Elapsed == 0) { - Elapsed = 1; - } - - ulReturn = Elapsed; - } else { - ulReturn = SYS_ARCH_TIMEOUT; - } - } else { // must block without a timeout - while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); - - EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; - - if (Elapsed == 0) { - Elapsed = 1; - } - - ulReturn = Elapsed; - } - - return ulReturn ; // return time blocked -} - -/*-----------------------------------------------------------------------------------*/ -// Creates an empty mailbox. -err_t ICACHE_FLASH_ATTR -sys_mbox_new(sys_mbox_t *mbox, int size) -{ - err_t xReturn = ERR_MEM; - - *mbox = xQueueCreate(size, sizeof(void *)); - - if (*mbox != NULL) { - xReturn = ERR_OK; - } - - return xReturn; -} - -/*-----------------------------------------------------------------------------------*/ -/* - 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 ICACHE_FLASH_ATTR -sys_mbox_free(sys_mbox_t *mbox) -{ - if (uxQueueMessagesWaiting(*mbox)) { - /* Line for breakpoint. Should never break here! */ -// __asm volatile ( "NOP" ); - } - - vQueueDelete(*mbox); -} - -/*-----------------------------------------------------------------------------------*/ -// Posts the "msg" to the mailbox. -void ICACHE_FLASH_ATTR -sys_mbox_post(sys_mbox_t *mbox, void *msg) -{ - while (xQueueSendToBack(*mbox, &msg, portMAX_DELAY) != pdTRUE); -} - -err_t ICACHE_FLASH_ATTR -sys_mbox_trypost(sys_mbox_t *mbox, void *msg) -{ - err_t xReturn; - - if (xQueueSend(*mbox, &msg, (portTickType)0) == pdPASS) { - xReturn = ERR_OK; - } else { - xReturn = ERR_MEM; - } - - return xReturn; -} - -/*-----------------------------------------------------------------------------------*/ -/* - 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). 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 ICACHE_FLASH_ATTR -sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) -{ - void *dummyptr; - portTickType StartTime, EndTime, Elapsed; - unsigned long ulReturn; - - StartTime = xTaskGetTickCount(); - - if (msg == NULL) { - msg = &dummyptr; - } - - if (timeout != 0) { - if (pdTRUE == xQueueReceive(*mbox, &(*msg), timeout / portTICK_RATE_MS)) { - EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; - - if (Elapsed == 0) { - Elapsed = 1; - } - - ulReturn = Elapsed; - } else { // timed out blocking for message - *msg = NULL; - ulReturn = SYS_ARCH_TIMEOUT; - } - } else { // block forever for a message. - while (pdTRUE != xQueueReceive(*mbox, &(*msg), portMAX_DELAY)); - - EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; - - if (Elapsed == 0) { - Elapsed = 1; - } - - ulReturn = Elapsed; - } - - return ulReturn ; // return time blocked TBD test -} - -u32_t ICACHE_FLASH_ATTR -sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) -{ - void *pvDummy; - unsigned long ulReturn; - - if (msg == NULL) { - msg = &pvDummy; - } - - if (pdTRUE == xQueueReceive(*mbox, &(*msg), 0)) { - ulReturn = ERR_OK; - } else { - ulReturn = SYS_MBOX_EMPTY; - } - - return ulReturn; -} - -/** Create a new mutex - * @param mutex pointer to the mutex to create - * @return a new mutex */ -err_t ICACHE_FLASH_ATTR -sys_mutex_new(sys_mutex_t *pxMutex) -{ - err_t xReturn = ERR_MEM; - - *pxMutex = xSemaphoreCreateMutex(); - - if (*pxMutex != NULL) { - xReturn = ERR_OK; - - } else { - ; - } - - return xReturn; -} - -/** Lock a mutex - * @param mutex the mutex to lock */ -void ICACHE_FLASH_ATTR -sys_mutex_lock(sys_mutex_t *pxMutex) -{ - while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS); -} - -/** Unlock a mutex - * @param mutex the mutex to unlock */ -void ICACHE_FLASH_ATTR -sys_mutex_unlock(sys_mutex_t *pxMutex) -{ - xSemaphoreGive(*pxMutex); -} - - -/** Delete a semaphore - * @param mutex the mutex to delete */ -void ICACHE_FLASH_ATTR -sys_mutex_free(sys_mutex_t *pxMutex) -{ - vQueueDelete(*pxMutex); -} - -u32_t ICACHE_FLASH_ATTR -sys_now(void) -{ - return xTaskGetTickCount(); -} - -/*-----------------------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -// TBD -/*-----------------------------------------------------------------------------------*/ -/* - Starts a new thread 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 id of the new thread is returned. Both the id and - the priority are system dependent. -*/ -sys_thread_t ICACHE_FLASH_ATTR -sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio) -{ - xTaskHandle CreatedTask; - portBASE_TYPE result; - - result = xTaskCreate(thread, (signed char *)name, stacksize, arg, prio, &CreatedTask); - - if (result == pdPASS) { - return CreatedTask; - } else { - return NULL; - } -} - -/* - 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. -*/ -sys_prot_t ICACHE_FLASH_ATTR -sys_arch_protect(void) -{ - vPortEnterCritical(); - return (sys_prot_t) 1; -} - -/* - 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. -*/ -void ICACHE_FLASH_ATTR -sys_arch_unprotect(sys_prot_t pval) -{ - (void) pval; - vPortExitCritical(); -} - -/* - * Prints an assertion messages and aborts execution. - */ -void ICACHE_FLASH_ATTR -sys_arch_assert(const char *file, int line) -{ - printf("\nAssertion: %d in %s\n", line, file); - - while(1); -} - diff --git a/third_party/lwip/core/Makefile b/third_party/lwip/core/Makefile deleted file mode 100644 index 9915910..0000000 --- a/third_party/lwip/core/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = liblwipcore.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/third_party/lwip/core/def.c b/third_party/lwip/core/def.c deleted file mode 100644 index 943de19..0000000 --- a/third_party/lwip/core/def.c +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @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) - * #define LWIP_PLATFORM_HTONL(x) - * - * 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -lwip_ntohl(u32_t n) -{ - return lwip_htonl(n); -} - -#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/third_party/lwip/core/dhcp.c b/third_party/lwip/core/dhcp.c deleted file mode 100644 index 05ceea6..0000000 --- a/third_party/lwip/core/dhcp.c +++ /dev/null @@ -1,1779 +0,0 @@ -/** - * @file - * Dynamic Host Configuration Protocol client - * - */ - -/* - * - * Copyright (c) 2001-2004 Leon Woestenberg - * 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 - * - * 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 - * - * - * 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/dhcp.h" -#include "lwip/autoip.h" -#include "lwip/dns.h" -#include "netif/etharp.h" - -#include - -/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using - * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) - */ -#ifndef DHCP_CREATE_RAND_XID -#define DHCP_CREATE_RAND_XID 1 -#endif - -/** 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]; - -#ifdef DHCP_GLOBAL_XID -static u32_t xid; -static u8_t xid_initialised; -#endif /* DHCP_GLOBAL_XID */ - -#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); -#if LWIP_NETIF_HOSTNAME -static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); -#endif /* LWIP_NETIF_HOSTNAME */ -/* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 - dhcp_option_hostname(dhcp, netif); -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ - for(n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) { - 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); - } -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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\n")); - /* 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; - } - ip_set_option(dhcp->pcb, 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 ICACHE_FLASH_ATTR -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; - ip_set_option(dhcp.pcb, 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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); - - dhcp->p_out->type = PBUF_RAM; - - 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 ICACHE_FLASH_ATTR -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 we have sub 1 minute lease, t2 and t1 will kick in at the same time. */ - if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { - dhcp->t1_timeout = 0; - } - - 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))); - - system_station_got_ip_set(&dhcp->offered_ip_addr, &sn_mask, &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 ICACHE_FLASH_ATTR -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 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 - -#if LWIP_NETIF_HOSTNAME - dhcp_option_hostname(dhcp, netif); -#endif /* LWIP_NETIF_HOSTNAME */ - - /* 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 ICACHE_FLASH_ATTR -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 - dhcp_option_hostname(dhcp, netif); -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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")); - if (dhcp == NULL) { - return ERR_ARG; - } - - /* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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)); -} - -#if LWIP_NETIF_HOSTNAME -static void ICACHE_FLASH_ATTR -dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) -{ - if (netif->hostname != NULL) { - size_t namelen = strlen(netif->hostname); - if (namelen > 0) { - u8_t len; - const char *p = netif->hostname; - /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME - and 1 byte for trailer) */ - size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; - LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); - len = LWIP_MIN(namelen, available); - dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len); - while (len--) { - dhcp_option_byte(dhcp, *p++); - } - } - } -} -#endif /* LWIP_NETIF_HOSTNAME */ - -/** - * 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 ICACHE_FLASH_ATTR -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_ERROR("len == 4", len == 4, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; - break; - case(DHCP_OPTION_ROUTER): - decode_len = 4; /* only copy the first given router */ - LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_ROUTER; - break; - case(DHCP_OPTION_DNS_SERVER): - /* special case: there might be more than one server */ - LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;); - /* limit number of DNS servers */ - decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); - LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_DNS_SERVER; - break; - case(DHCP_OPTION_LEASE_TIME): - LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_LEASE_TIME; - break; - case(DHCP_OPTION_OVERLOAD): - LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_OVERLOAD; - break; - case(DHCP_OPTION_MESSAGE_TYPE): - LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_MSG_TYPE; - break; - case(DHCP_OPTION_SERVER_ID): - LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_SERVER_ID; - break; - case(DHCP_OPTION_T1): - LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); - decode_idx = DHCP_OPTION_IDX_T1; - break; - case(DHCP_OPTION_T2): - LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); - 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); - if (!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_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;); - 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_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); - 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; - if ((offset < offset_max) && offset_max) { - q = q->next; - LWIP_ASSERT("next pbuf was null", q); - options = (u8_t*)q->payload; - } else { - /* We've run out of bytes, probably no end marker. Don't proceed. */ - break; - } - } - } - /* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ -#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) - static u32_t xid; -#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ - static u32_t xid = 0xABCD0000; -#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ -#else - 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) { -#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) - xid = LWIP_RAND(); -#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ - xid++; -#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ - } - 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 && i < NETIF_MAX_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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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)) && - (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/third_party/lwip/core/dns.c b/third_party/lwip/core/dns.c deleted file mode 100644 index 77142e8..0000000 --- a/third_party/lwip/core/dns.c +++ /dev/null @@ -1,988 +0,0 @@ -/** - * @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 - -/** 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -dns_setserver(u8_t numdns, ip_addr_t *dnsserver) -{ - 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 + 1 + - SIZEOF_DNS_QUERY, PBUF_RAM); - if (p != NULL) { - u16_t realloc_size; - 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 */ - realloc_size = (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))); - LWIP_ASSERT("p->tot_len >= realloc_size", p->tot_len >= realloc_size); - pbuf_realloc(p, realloc_size); - - /* 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 ICACHE_FLASH_ATTR -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+1numdns+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) || (--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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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); - } - if (pEntry->ttl == 0) { - /* RFC 883, page 29: "Zero values are - interpreted to mean that the RR can only be used for the - transaction in progress, and should not be cached." - -> flush this entry now */ - goto flushentry; - } - /* 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); - } -flushentry: - /* 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 hostnamelen length of the hostname - * @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 ICACHE_FLASH_ATTR -dns_enqueue(const char *name, size_t hostnamelen, 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(hostnamelen, 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 ICACHE_FLASH_ATTR -dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, - void *callback_arg) -{ - u32_t ipaddr; - size_t hostnamelen; - /* 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])) { - return ERR_ARG; - } - hostnamelen = strlen(hostname); - if (hostnamelen >= 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, hostnamelen, found, callback_arg); -} - -#endif /* LWIP_DNS */ diff --git a/third_party/lwip/core/inet_chksum.c b/third_party/lwip/core/inet_chksum.c deleted file mode 100644 index c956d6b..0000000 --- a/third_party/lwip/core/inet_chksum.c +++ /dev/null @@ -1,545 +0,0 @@ -/** - * @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 - * - */ - -#include "lwip/opt.h" - -#include "lwip/inet_chksum.h" -#include "lwip/def.h" - -#include -#include - -/* 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 - * - * 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 -u16_t lwip_standard_chksum(void *dataptr, int len); -#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) - */ -u16_t ICACHE_FLASH_ATTR -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) - */ - -u16_t ICACHE_FLASH_ATTR -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 - */ - -u16_t ICACHE_FLASH_ATTR -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 - -/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ -static u16_t ICACHE_FLASH_ATTR -inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) -{ - struct pbuf *q; - u8_t 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); - } - - 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 ICACHE_FLASH_ATTR -inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, - ip_addr_t *src, ip_addr_t *dest) -{ - u32_t acc; - u32_t addr; - - 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); - /* fold down to 16 bits */ - acc = FOLD_U32T(acc); - acc = FOLD_U32T(acc); - - return inet_cksum_pseudo_base(p, proto, proto_len, acc); -} -#if LWIP_IPV6 -/** - * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. - * IPv6 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 ipv6 address (used for checksum of pseudo header) - * @param dst destination ipv6 address (used for checksum of pseudo header) - * @param proto ipv6 protocol/next header (used for checksum of pseudo header) - * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) - * @return checksum (as u16_t) to be saved directly in the protocol header - */ -u16_t ICACHE_FLASH_ATTR -ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, - ip6_addr_t *src, ip6_addr_t *dest) -{ - u32_t acc = 0; - u32_t addr; - u8_t addr_part; - - for (addr_part = 0; addr_part < 4; addr_part++) { - addr = src->addr[addr_part]; - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); - addr = dest->addr[addr_part]; - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); - } - /* fold down to 16 bits */ - acc = FOLD_U32T(acc); - acc = FOLD_U32T(acc); - - return inet_cksum_pseudo_base(p, proto, proto_len, acc); -} -#endif /* LWIP_IPV6 */ - -/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ -static u16_t ICACHE_FLASH_ATTR -inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, - u16_t chksum_len, u32_t acc) -{ - struct pbuf *q; - u8_t swapped = 0; - u16_t chklen; - - /* 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); - } - - 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_partial: - * - * 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 ICACHE_FLASH_ATTR -inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, - u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest) -{ - u32_t acc; - u32_t addr; - - 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); - /* fold down to 16 bits */ - acc = FOLD_U32T(acc); - acc = FOLD_U32T(acc); - - return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); -} - -#if LWIP_IPV6 -/** - * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. - * IPv6 addresses are expected to be in network byte order. Will only compute for a - * portion of the payload. - * - * @param p chain of pbufs over that a checksum should be calculated (ip data part) - * @param src source ipv6 address (used for checksum of pseudo header) - * @param dst destination ipv6 address (used for checksum of pseudo header) - * @param proto ipv6 protocol/next header (used for checksum of pseudo header) - * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) - * @param chksum_len number of payload bytes used to compute chksum - * @return checksum (as u16_t) to be saved directly in the protocol header - */ -u16_t ICACHE_FLASH_ATTR -ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, - u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest) -{ - u32_t acc = 0; - u32_t addr; - u8_t addr_part; - - for (addr_part = 0; addr_part < 4; addr_part++) { - addr = src->addr[addr_part]; - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); - addr = dest->addr[addr_part]; - acc += (addr & 0xffffUL); - acc += ((addr >> 16) & 0xffffUL); - } - /* fold down to 16 bits */ - acc = FOLD_U32T(acc); - acc = FOLD_U32T(acc); - - return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); -} -#endif /* LWIP_IPV6 */ - -/* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/init.c b/third_party/lwip/core/init.c deleted file mode 100644 index b8b98ed..0000000 --- a/third_party/lwip/core/init.c +++ /dev/null @@ -1,345 +0,0 @@ -/** - * @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 - * - */ - -#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" -#include "lwip/ip6.h" -#include "lwip/nd6.h" -#include "lwip/mld6.h" -#include "lwip/api.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_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 !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ -#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_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_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 -/* 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 + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) - #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 -#endif /* !MEMP_MEM_MALLOC */ -#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_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) - #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 -#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 (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 || LWIP_IPV6) && !defined(LWIP_RAND) - #error "When using IGMP or IPv6, 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 -#if LWIP_NETCONN && LWIP_TCP -#if NETCONN_COPY != TCP_WRITE_FLAG_COPY - #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" -#endif -#if NETCONN_MORE != TCP_WRITE_FLAG_MORE - #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" -#endif -#endif /* LWIP_NETCONN && LWIP_TCP */ -#if LWIP_SOCKET -/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ -#if SO_ACCEPTCONN != SOF_ACCEPTCONN - #error "SO_ACCEPTCONN != SOF_ACCEPTCONN" -#endif -#if SO_REUSEADDR != SOF_REUSEADDR - #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" -#endif -#if SO_KEEPALIVE != SOF_KEEPALIVE - #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" -#endif -#if SO_BROADCAST != SOF_BROADCAST - #error "WARNING: SO_BROADCAST != SOF_BROADCAST" -#endif -#if SO_LINGER != SOF_LINGER - #error "WARNING: SO_LINGER != SOF_LINGER" -#endif -#endif /* LWIP_SOCKET */ - - -/* 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 - -#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS -#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 -#endif -#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS -#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 -#endif - -/* MEMP sanity checks */ -#if !LWIP_DISABLE_MEMP_SANITY_CHECKS -#if LWIP_NETCONN -#if MEMP_MEM_MALLOC -#if !MEMP_NUM_NETCONN && LWIP_SOCKET -#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" -#endif -#else /* MEMP_MEM_MALLOC */ -#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) -#error "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. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." -#endif -#endif /* MEMP_MEM_MALLOC */ -#endif /* LWIP_NETCONN */ -#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ - -/* TCP sanity checks */ -#if !LWIP_DISABLE_TCP_SANITY_CHECKS -#if LWIP_TCP -#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) - #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if TCP_SND_BUF < (2 * TCP_MSS) - #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) - #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if TCP_SNDLOWAT >= TCP_SND_BUF - #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN - #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) - #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) - #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#if TCP_WND < TCP_MSS - #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." -#endif -#endif /* LWIP_TCP */ -#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ - -/** - * Perform Sanity check of user-configurable values, and initialize all modules. - */ -void ICACHE_FLASH_ATTR -lwip_init(void) -{ - /* Modules initialization */ - stats_init(); -#if !NO_SYS - sys_init(); -#endif /* !NO_SYS */ - 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_IPV6 - ip6_init(); - nd6_init(); -#if LWIP_IPV6_MLD - mld6_init(); -#endif /* LWIP_IPV6_MLD */ -#endif /* LWIP_IPV6 */ - -#if LWIP_TIMERS - sys_timeouts_init(); -#endif /* LWIP_TIMERS */ -} diff --git a/third_party/lwip/core/ipv4/Makefile b/third_party/lwip/core/ipv4/Makefile deleted file mode 100644 index 2d818fe..0000000 --- a/third_party/lwip/core/ipv4/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = liblwipipv4.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/third_party/lwip/core/ipv4/autoip.c b/third_party/lwip/core/ipv4/autoip.c deleted file mode 100644 index fd61792..0000000 --- a/third_party/lwip/core/ipv4/autoip.c +++ /dev/null @@ -1,528 +0,0 @@ -/** - * @file - * AutoIP Automatic LinkLocal IP Configuration - * - */ - -/* - * - * Copyright (c) 2007 Dominik Spies - * 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 - * - * 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 - * - */ - -/******************************************************************************* - * 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 -#include - -/* 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); - - -/** 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -autoip_restart(struct netif *netif) -{ - netif->autoip->tried_llipaddr++; - autoip_start(netif); -} - -/** - * Handle a IP address conflict after an ARP conflict detection - */ -static void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -autoip_arp_probe(struct netif *netif) -{ - return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, - (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, - &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 ICACHE_FLASH_ATTR -autoip_arp_announce(struct netif *netif) -{ - return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, - (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, - &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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/ipv4/icmp.c b/third_party/lwip/core/ipv4/icmp.c deleted file mode 100644 index b0e6474..0000000 --- a/third_party/lwip/core/ipv4/icmp.c +++ /dev/null @@ -1,338 +0,0 @@ -/** - * @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 - * - */ - -/* 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 - -/** 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 icmp header - * @param inp the netif on which this packet was received - */ -void ICACHE_FLASH_ATTR -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 *)ip_current_header(); - hlen = IPH_HL(iphdr) * 4; - if (p->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(ip_current_dest_addr())) { - accepted = 0; - } -#endif /* LWIP_MULTICAST_PING */ -#if !LWIP_BROADCAST_PING - /* broadcast destination address? */ - if (ip_addr_isbroadcast(ip_current_dest_addr(), 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); -#if CHECKSUM_GEN_ICMP - /* 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); - } -#else /* CHECKSUM_GEN_ICMP */ - iecho->chksum = 0; -#endif /* CHECKSUM_GEN_ICMP */ - - /* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/ipv4/igmp.c b/third_party/lwip/core/ipv4/igmp.c deleted file mode 100644 index 223b2df..0000000 --- a/third_party/lwip/core/ipv4/igmp.c +++ /dev/null @@ -1,805 +0,0 @@ -/** - * @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_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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 igmp header - * @param inp network interface on which the packet was received - * @param dest destination ip address of the igmp packet - */ -void ICACHE_FLASH_ATTR -igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) -{ - 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 */ - if (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, &(ip_current_header()->src)); - LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); - ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -igmp_start_timer(struct igmp_group *group, u8_t max_time) -{ - /* ensure the input value is > 0 */ - if (max_time == 0) { - max_time = 1; - } -#ifdef LWIP_RAND - /* ensure the random value is > 0 */ - group->timer = (LWIP_RAND() % max_time); -#endif /* LWIP_RAND */ -} - -/** - * Delaying membership report for a group if necessary - * - * @param group the igmp_group for which "delaying" membership report - * @param maxresp query delay - */ -static void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/ipv4/ip4.c b/third_party/lwip/core/ipv4/ip4.c deleted file mode 100644 index 8d3a34d..0000000 --- a/third_party/lwip/core/ipv4/ip4.c +++ /dev/null @@ -1,922 +0,0 @@ -/** - * @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 - * - */ - -#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 - -/** 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(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 */ - -/** Global data for both IPv4 and IPv6 */ -struct ip_globals ip_data; - -/** 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 * ICACHE_FLASH_ATTR -ip_route(ip_addr_t *dest) -{ - struct netif *netif; - -#ifdef LWIP_HOOK_IP4_ROUTE - netif = LWIP_HOOK_IP4_ROUTE(dest); - if (netif != NULL) { - return netif; - } -#endif - - /* 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 (!ip_addr_isbroadcast(dest, netif) && netif != netif_default) { - 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 -/** - * Determine whether an IP address is in a reserved set of addresses - * that may not be forwarded, or whether datagrams to that destination - * may be forwarded. - * @param p the packet to forward - * @param dest the destination IP address - * @return 1: can forward 0: discard - */ -static int ICACHE_FLASH_ATTR -ip_canforward(struct pbuf *p) -{ - u32_t addr = htonl(ip4_addr_get_u32(ip_current_dest_addr())); - - if (p->flags & PBUF_FLAG_LLBCAST) { - /* don't route link-layer broadcasts */ - return 0; - } - if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { - /* don't route link-layer multicasts unless the destination address is an IP - multicast address */ - return 0; - } - if (IP_EXPERIMENTAL(addr)) { - return 0; - } - if (IP_CLASSA(addr)) { - u32_t net = addr & IP_CLASSA_NET; - if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { - /* don't route loopback packets */ - return 0; - } - } - return 1; -} - -/** - * 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 ICACHE_FLASH_ATTR -ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) -{ - struct netif *netif; - - PERF_START; - - if (!ip_canforward(p)) { - goto return_noroute; - } - - /* RFC3927 2.7: do not forward link-local addresses */ - if (ip_addr_islinklocal(ip_current_dest_addr())) { - LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", - ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), - ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); - goto return_noroute; - } - - /* Find network interface where to forward this IP packet to. */ - netif = ip_route(ip_current_dest_addr()); - 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(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), - ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); - /* @todo: send ICMP_DUR_NET? */ - goto return_noroute; - } -#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF - /* 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; - } -#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ - - /* 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(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), - ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); - - IP_STATS_INC(ip.fw); - IP_STATS_INC(ip.xmit); - snmp_inc_ipforwdatagrams(); - - PERF_STOP("ip_forward"); - /* don't fragment if interface has mtu set to 0 [loopif] */ - if (netif->mtu && (p->tot_len > netif->mtu)) { - if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { -#if IP_FRAG - ip_frag(p, netif, ip_current_dest_addr()); -#else /* IP_FRAG */ - /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */ -#endif /* IP_FRAG */ - } else { - /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */ - icmp_dest_unreach(p, ICMP_DUR_FRAG); - } - return; - } - /* transmit pbuf on chosen interface */ - netif->output(netif, p, ip_current_dest_addr()); - 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 ICACHE_FLASH_ATTR -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; - } - -#ifdef LWIP_HOOK_IP4_INPUT - if (LWIP_HOOK_IP4_INPUT(p, inp)) { - /* the packet has been eaten */ - return ERR_OK; - } -#endif - - /* 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(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest); - ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src); - - /* match packet against an interface, i.e. is this packet for us? */ -#if LWIP_IGMP - if (ip_addr_ismulticast(ip_current_dest_addr())) { - if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) { - 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(ip_current_dest_addr(), &(netif->ip_addr)) || - /* or broadcast on this interface network address? */ - ip_addr_isbroadcast(ip_current_dest_addr(), 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(ip_current_dest_addr(), &(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(ip_current_src_addr())) -#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ - { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) || - (ip_addr_ismulticast(ip_current_src_addr()))) { - /* 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(ip_current_dest_addr(), 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)); - - ip_data.current_netif = inp; - ip_data.current_ip4_header = iphdr; - ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; - -#if LWIP_RAW - /* raw input did not eat the packet? */ - if (raw_input(p, inp) == 0) -#endif /* LWIP_RAW */ - { - pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ - - 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, ip_current_dest_addr()); - break; -#endif /* LWIP_IGMP */ - default: -#if LWIP_ICMP - /* send ICMP destination protocol unreachable unless is was a broadcast */ - if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && - !ip_addr_ismulticast(ip_current_dest_addr())) { - pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */ - 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(); - } - } - - /* @todo: this is not really necessary... */ - ip_data.current_netif = NULL; - ip_data.current_ip4_header = NULL; - ip_data.current_ip_header_tot_len = 0; - ip_addr_set_any(ip_current_src_addr()); - ip_addr_set_any(ip_current_dest_addr()); - - 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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_VHL_SET(iphdr, 4, ip_hlen / 4); - IPH_TOS_SET(iphdr, tos); -#if CHECKSUM_GEN_IP_INLINE - chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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_SET_HWADDRHINT(netif, addr_hint); - err = ip_output_if(p, src, dest, ttl, tos, proto, netif); - NETIF_SET_HWADDRHINT(netif, 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 ICACHE_FLASH_ATTR -ip_debug_print(struct pbuf *p) -{ - struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; - - 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/third_party/lwip/core/ipv4/ip4_addr.c b/third_party/lwip/core/ipv4/ip4_addr.c deleted file mode 100644 index 64783ee..0000000 --- a/third_party/lwip/core/ipv4/ip4_addr.c +++ /dev/null @@ -1,313 +0,0 @@ -/** - * @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 - * - */ - -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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/third_party/lwip/core/ipv4/ip_frag.c b/third_party/lwip/core/ipv4/ip_frag.c deleted file mode 100644 index 8f14b4d..0000000 --- a/third_party/lwip/core/ipv4/ip_frag.c +++ /dev/null @@ -1,863 +0,0 @@ -/** - * @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 - * Simon Goldschmidt - * original reassembly code by Adam Dunkels - * - */ - -#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 - -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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* ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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* ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/ipv6/README b/third_party/lwip/core/ipv6/README deleted file mode 100644 index a59caef..0000000 --- a/third_party/lwip/core/ipv6/README +++ /dev/null @@ -1 +0,0 @@ -IPv6 support in lwIP is very experimental. diff --git a/third_party/lwip/core/ipv6/dhcp6.c b/third_party/lwip/core/ipv6/dhcp6.c deleted file mode 100644 index bb0d574..0000000 --- a/third_party/lwip/core/ipv6/dhcp6.c +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file - * - * DHCPv6. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" - -#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/ip6_addr.h" -#include "lwip/def.h" - - -#endif /* LWIP_IPV6_DHCP6 */ diff --git a/third_party/lwip/core/ipv6/ethip6.c b/third_party/lwip/core/ipv6/ethip6.c deleted file mode 100644 index bde2334..0000000 --- a/third_party/lwip/core/ipv6/ethip6.c +++ /dev/null @@ -1,193 +0,0 @@ -/** - * @file - * - * Ethernet output for IPv6. Uses ND tables for link-layer addressing. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" - -#if LWIP_IPV6 && LWIP_ETHERNET - -#include "lwip/ethip6.h" -#include "lwip/nd6.h" -#include "lwip/pbuf.h" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/inet_chksum.h" -#include "lwip/netif.h" -#include "lwip/icmp6.h" - -#include - -#define ETHTYPE_IPV6 0x86DD - -/** The ethernet address */ -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/bpstruct.h" -#endif -PACK_STRUCT_BEGIN -struct eth_addr { - PACK_STRUCT_FIELD(u8_t addr[6]); -} PACK_STRUCT_STRUCT; -PACK_STRUCT_END -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/epstruct.h" -#endif - -/** Ethernet header */ -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/bpstruct.h" -#endif -PACK_STRUCT_BEGIN -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) - -/** - * Send an IPv6 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 -ethip6_send(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 6 for ethip6!", - (netif->hwaddr_len == 6)); - SMEMCPY(ðhdr->dest, dst, 6); - SMEMCPY(ðhdr->src, src, 6); - ethhdr->type = PP_HTONS(ETHTYPE_IPV6); - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p)); - /* send the packet */ - return netif->linkoutput(netif, p); -} - -/** - * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. - * - * For IPv6 multicast, corresponding Ethernet addresses - * are selected and the packet is transmitted on the link. - * - * For unicast addresses, ... - * - * @TODO anycast addresses - * - * @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 ip6addr 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 -ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr) -{ - struct eth_addr dest; - s8_t i; - - /* 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")); - return ERR_BUF; - } - - /* multicast destination IP address? */ - if (ip6_addr_ismulticast(ip6addr)) { - /* Hash IP multicast address to MAC address.*/ - dest.addr[0] = 0x33; - dest.addr[1] = 0x33; - dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0]; - dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1]; - dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2]; - dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3]; - - /* Send out. */ - return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); - } - - /* We have a unicast destination IP address */ - /* TODO anycast? */ - /* Get next hop record. */ - i = nd6_get_next_hop_entry(ip6addr, netif); - if (i < 0) { - /* failed to get a next hop neighbor record. */ - return ERR_MEM; - } - - /* Now that we have a destination record, send or queue the packet. */ - if (neighbor_cache[i].state == ND6_STALE) { - /* Switch to delay state. */ - neighbor_cache[i].state = ND6_DELAY; - neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; - } - /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */ - if ((neighbor_cache[i].state == ND6_REACHABLE) || - (neighbor_cache[i].state == ND6_DELAY) || - (neighbor_cache[i].state == ND6_PROBE)) { - - /* Send out. */ - SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); - return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); - } - - /* We should queue packet on this interface. */ - pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR); - return nd6_queue_packet(i, q); -} - -#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/third_party/lwip/core/ipv6/icmp6.c b/third_party/lwip/core/ipv6/icmp6.c deleted file mode 100644 index f876804..0000000 --- a/third_party/lwip/core/ipv6/icmp6.c +++ /dev/null @@ -1,338 +0,0 @@ -/** - * @file - * - * IPv6 version of ICMP, as per RFC 4443. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" - -#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/icmp6.h" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/inet_chksum.h" -#include "lwip/pbuf.h" -#include "lwip/netif.h" -#include "lwip/nd6.h" -#include "lwip/mld6.h" -#include "lwip/ip.h" -#include "lwip/stats.h" - -#include - -#ifndef LWIP_ICMP6_DATASIZE -#define LWIP_ICMP6_DATASIZE 8 -#endif -#if LWIP_ICMP6_DATASIZE == 0 -#define LWIP_ICMP6_DATASIZE 8 -#endif - -/* Forward declarations */ -static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); - - -/** - * Process an input ICMPv6 message. Called by ip6_input. - * - * Will generate a reply for echo requests. Other messages are forwarded - * to nd6_input, or mld6_input. - * - * @param p the mld packet, p->payload pointing to the icmpv6 header - * @param inp the netif on which this packet was received - */ -void -icmp6_input(struct pbuf *p, struct netif *inp) -{ - struct icmp6_hdr *icmp6hdr; - struct pbuf * r; - ip6_addr_t * reply_src; - - ICMP6_STATS_INC(icmp6.recv); - - /* Check that ICMPv6 header fits in payload */ - if (p->len < sizeof(struct icmp6_hdr)) { - /* drop short packets */ - pbuf_free(p); - ICMP6_STATS_INC(icmp6.lenerr); - ICMP6_STATS_INC(icmp6.drop); - return; - } - - icmp6hdr = (struct icmp6_hdr *)p->payload; - -#if LWIP_ICMP6_CHECKSUM_CHECK - if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), - ip6_current_dest_addr()) != 0) { - /* Checksum failed */ - pbuf_free(p); - ICMP6_STATS_INC(icmp6.chkerr); - ICMP6_STATS_INC(icmp6.drop); - return; - } -#endif /* LWIP_ICMP6_CHECKSUM_CHECK */ - - switch (icmp6hdr->type) { - case ICMP6_TYPE_NA: /* Neighbor advertisement */ - case ICMP6_TYPE_NS: /* Neighbor solicitation */ - case ICMP6_TYPE_RA: /* Router advertisement */ - case ICMP6_TYPE_RD: /* Redirect */ - case ICMP6_TYPE_PTB: /* Packet too big */ - nd6_input(p, inp); - return; - break; - case ICMP6_TYPE_RS: -#if LWIP_IPV6_FORWARD - /* TODO implement router functionality */ -#endif - break; -#if LWIP_IPV6_MLD - case ICMP6_TYPE_MLQ: - case ICMP6_TYPE_MLR: - case ICMP6_TYPE_MLD: - mld6_input(p, inp); - return; - break; -#endif - case ICMP6_TYPE_EREQ: -#if !LWIP_MULTICAST_PING - /* multicast destination address? */ - if (ip6_addr_ismulticast(ip6_current_dest_addr())) { - /* drop */ - pbuf_free(p); - ICMP6_STATS_INC(icmp6.drop); - return; - } -#endif /* LWIP_MULTICAST_PING */ - - /* Allocate reply. */ - r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); - if (r == NULL) { - /* drop */ - pbuf_free(p); - ICMP6_STATS_INC(icmp6.memerr); - return; - } - - /* Copy echo request. */ - if (pbuf_copy(r, p) != ERR_OK) { - /* drop */ - pbuf_free(p); - pbuf_free(r); - ICMP6_STATS_INC(icmp6.err); - return; - } - - /* Determine reply source IPv6 address. */ -#if LWIP_MULTICAST_PING - if (ip6_addr_ismulticast(ip6_current_dest_addr())) { - reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); - if (reply_src == NULL) { - /* drop */ - pbuf_free(p); - pbuf_free(r); - ICMP6_STATS_INC(icmp6.rterr); - return; - } - } - else -#endif /* LWIP_MULTICAST_PING */ - { - reply_src = ip6_current_dest_addr(); - } - - /* Set fields in reply. */ - ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; - ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; - ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, - IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); - - /* Send reply. */ - ICMP6_STATS_INC(icmp6.xmit); - ip6_output_if(r, reply_src, ip6_current_src_addr(), - LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); - pbuf_free(r); - - break; - default: - ICMP6_STATS_INC(icmp6.proterr); - ICMP6_STATS_INC(icmp6.drop); - break; - } - - pbuf_free(p); -} - - -/** - * Send an icmpv6 'destination unreachable' packet. - * - * @param p the input packet for which the 'unreachable' should be sent, - * p->payload pointing to the IPv6 header - * @param c ICMPv6 code for the unreachable type - */ -void -icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) -{ - icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); -} - -/** - * Send an icmpv6 'packet too big' packet. - * - * @param p the input packet for which the 'packet too big' should be sent, - * p->payload pointing to the IPv6 header - * @param mtu the maximum mtu that we can accept - */ -void -icmp6_packet_too_big(struct pbuf *p, u32_t mtu) -{ - icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); -} - -/** - * Send an icmpv6 'time exceeded' packet. - * - * @param p the input packet for which the 'unreachable' should be sent, - * p->payload pointing to the IPv6 header - * @param c ICMPv6 code for the time exceeded type - */ -void -icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) -{ - icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); -} - -/** - * Send an icmpv6 'parameter problem' packet. - * - * @param p the input packet for which the 'param problem' should be sent, - * p->payload pointing to the IP header - * @param c ICMPv6 code for the param problem type - * @param pointer the pointer to the byte where the parameter is found - */ -void -icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) -{ - icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); -} - -/** - * Send an ICMPv6 packet in response to an incoming packet. - * - * @param p the input packet for which the response should be sent, - * p->payload pointing to the IPv6 header - * @param code Code of the ICMPv6 header - * @param data Additional 32-bit parameter in the ICMPv6 header - * @param type Type of the ICMPv6 header - */ -static void -icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) -{ - struct pbuf *q; - struct icmp6_hdr *icmp6hdr; - ip6_addr_t *reply_src, *reply_dest; - ip6_addr_t reply_src_local, reply_dest_local; - struct ip6_hdr *ip6hdr; - struct netif *netif; - - /* ICMPv6 header + IPv6 header + data */ - q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, - PBUF_RAM); - if (q == NULL) { - LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); - ICMP6_STATS_INC(icmp6.memerr); - return; - } - LWIP_ASSERT("check that first pbuf can hold icmp 6message", - (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); - - icmp6hdr = (struct icmp6_hdr *)q->payload; - icmp6hdr->type = type; - icmp6hdr->code = code; - icmp6hdr->data = data; - - /* copy fields from original packet */ - SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, - IP6_HLEN + LWIP_ICMP6_DATASIZE); - - /* Get the destination address and netif for this ICMP message. */ - if ((ip_current_netif() == NULL) || - ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { - /* Special case, as ip6_current_xxx is either NULL, or points - * to a different packet than the one that expired. - * We must use the addresses that are stored in the expired packet. */ - ip6hdr = (struct ip6_hdr *)p->payload; - /* copy from packed address to aligned address */ - ip6_addr_copy(reply_dest_local, ip6hdr->src); - ip6_addr_copy(reply_src_local, ip6hdr->dest); - reply_dest = &reply_dest_local; - reply_src = &reply_src_local; - netif = ip6_route(reply_src, reply_dest); - if (netif == NULL) { - /* drop */ - pbuf_free(q); - ICMP6_STATS_INC(icmp6.rterr); - return; - } - } - else { - netif = ip_current_netif(); - reply_dest = ip6_current_src_addr(); - - /* Select an address to use as source. */ - reply_src = ip6_select_source_address(netif, reply_dest); - if (reply_src == NULL) { - /* drop */ - pbuf_free(q); - ICMP6_STATS_INC(icmp6.rterr); - return; - } - } - - /* calculate checksum */ - icmp6hdr->chksum = 0; - icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, - reply_src, reply_dest); - - ICMP6_STATS_INC(icmp6.xmit); - ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); - pbuf_free(q); -} - -#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/third_party/lwip/core/ipv6/inet6.c b/third_party/lwip/core/ipv6/inet6.c deleted file mode 100644 index bd44208..0000000 --- a/third_party/lwip/core/ipv6/inet6.c +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file - * - * INET v6 addresses. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" - -#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/def.h" -#include "lwip/inet6.h" - -/** @see ip6_addr.c for implementation of functions. */ - -#endif /* LWIP_IPV6 */ diff --git a/third_party/lwip/core/ipv6/ip6.c b/third_party/lwip/core/ipv6/ip6.c deleted file mode 100644 index 4affcfd..0000000 --- a/third_party/lwip/core/ipv6/ip6.c +++ /dev/null @@ -1,1028 +0,0 @@ -/** - * @file - * - * IPv6 layer. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - - -#include "lwip/opt.h" - -#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/def.h" -#include "lwip/mem.h" -#include "lwip/netif.h" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/ip6_frag.h" -#include "lwip/icmp6.h" -#include "lwip/raw.h" -#include "lwip/udp.h" -#include "lwip/tcp_impl.h" -#include "lwip/dhcp6.h" -#include "lwip/nd6.h" -#include "lwip/mld6.h" -#include "lwip/debug.h" -#include "lwip/stats.h" - - -/** - * Finds the appropriate network interface for a given IPv6 address. It tries to select - * a netif following a sequence of heuristics: - * 1) if there is only 1 netif, return it - * 2) if the destination is a link-local address, try to match the src address to a netif. - * this is a tricky case because with multiple netifs, link-local addresses only have - * meaning within a particular subnet/link. - * 3) tries to match the destination subnet to a configured address - * 4) tries to find a router - * 5) tries to match the source address to the netif - * 6) returns the default netif, if configured - * - * @param src the source IPv6 address, if known - * @param dest the destination IPv6 address for which to find the route - * @return the netif on which to send to reach dest - */ -struct netif * -ip6_route(struct ip6_addr *src, struct ip6_addr *dest) -{ - struct netif *netif; - s8_t i; - - /* If single netif configuration, fast return. */ - if ((netif_list != NULL) && (netif_list->next == NULL)) { - return netif_list; - } - - /* Special processing for link-local addresses. */ - if (ip6_addr_islinklocal(dest)) { - if (ip6_addr_isany(src)) { - /* Use default netif. */ - return netif_default; - } - - /* Try to find the netif for the source address. */ - for(netif = netif_list; netif != NULL; netif = netif->next) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { - return netif; - } - } - } - - /* netif not found, use default netif */ - return netif_default; - } - - /* See if the destination subnet matches a configured address. */ - for(netif = netif_list; netif != NULL; netif = netif->next) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { - return netif; - } - } - } - - /* Get the netif for a suitable router. */ - i = nd6_select_router(dest, NULL); - if (i >= 0) { - if (default_router_list[i].neighbor_entry != NULL) { - if (default_router_list[i].neighbor_entry->netif != NULL) { - return default_router_list[i].neighbor_entry->netif; - } - } - } - - /* try with the netif that matches the source address. */ - if (!ip6_addr_isany(src)) { - for(netif = netif_list; netif != NULL; netif = netif->next) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { - return netif; - } - } - } - } - - /* no matching netif found, use default netif */ - return netif_default; -} - -/** - * Select the best IPv6 source address for a given destination - * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior - * is assumed. - * - * @param netif the netif on which to send a packet - * @param dest the destination we are trying to reach - * @return the most suitable source address to use, or NULL if no suitable - * source address is found - */ -ip6_addr_t * -ip6_select_source_address(struct netif *netif, ip6_addr_t * dest) -{ - ip6_addr_t * src = NULL; - u8_t i; - - /* If dest is link-local, choose a link-local source. */ - if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { - return netif_ip6_addr(netif, i); - } - } - } - - /* Choose a site-local with matching prefix. */ - if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && - ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { - return netif_ip6_addr(netif, i); - } - } - } - - /* Choose a unique-local with matching prefix. */ - if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && - ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { - return netif_ip6_addr(netif, i); - } - } - } - - /* Choose a global with best matching prefix. */ - if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_isglobal(netif_ip6_addr(netif, i))) { - if (src == NULL) { - src = netif_ip6_addr(netif, i); - } - else { - /* Replace src only if we find a prefix match. */ - /* TODO find longest matching prefix. */ - if ((!(ip6_addr_netcmp(src, dest))) && - ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { - src = netif_ip6_addr(netif, i); - } - } - } - } - if (src != NULL) { - return src; - } - } - - /* Last resort: see if arbitrary prefix matches. */ - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { - return netif_ip6_addr(netif, i); - } - } - - return NULL; -} - -#if LWIP_IPV6_FORWARD -/** - * Forwards an IPv6 packet. It finds an appropriate route for the - * packet, decrements the HL value of the packet, and outputs - * the packet on the appropriate interface. - * - * @param p the packet to forward (p->payload points to IP header) - * @param iphdr the IPv6 header of the input packet - * @param inp the netif on which this packet was received - */ -static void -ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) -{ - struct netif *netif; - - /* do not forward link-local addresses */ - if (ip6_addr_islinklocal(ip6_current_dest_addr())) { - LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); - IP6_STATS_INC(ip6.rterr); - IP6_STATS_INC(ip6.drop); - return; - } - - /* Find network interface where to forward this IP packet to. */ - netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr()); - if (netif == NULL) { - LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", - IP6_ADDR_BLOCK1(ip6_current_dest_addr()), - IP6_ADDR_BLOCK2(ip6_current_dest_addr()), - IP6_ADDR_BLOCK3(ip6_current_dest_addr()), - IP6_ADDR_BLOCK4(ip6_current_dest_addr()), - IP6_ADDR_BLOCK5(ip6_current_dest_addr()), - IP6_ADDR_BLOCK6(ip6_current_dest_addr()), - IP6_ADDR_BLOCK7(ip6_current_dest_addr()), - IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); -#if LWIP_ICMP6 - /* Don't send ICMP messages in response to ICMP messages */ - if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { - icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); - } -#endif /* LWIP_ICMP6 */ - IP6_STATS_INC(ip6.rterr); - IP6_STATS_INC(ip6.drop); - return; - } - /* Do not forward packets onto the same network interface on which - * they arrived. */ - if (netif == inp) { - LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); - IP6_STATS_INC(ip6.rterr); - IP6_STATS_INC(ip6.drop); - return; - } - - /* decrement HL */ - IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); - /* send ICMP6 if HL == 0 */ - if (IP6H_HOPLIM(iphdr) == 0) { -#if LWIP_ICMP6 - /* Don't send ICMP messages in response to ICMP messages */ - if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { - icmp6_time_exceeded(p, ICMP6_TE_HL); - } -#endif /* LWIP_ICMP6 */ - IP6_STATS_INC(ip6.drop); - return; - } - - if (netif->mtu && (p->tot_len > netif->mtu)) { -#if LWIP_ICMP6 - /* Don't send ICMP messages in response to ICMP messages */ - if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { - icmp6_packet_too_big(p, netif->mtu); - } -#endif /* LWIP_ICMP6 */ - IP6_STATS_INC(ip6.drop); - return; - } - - LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", - IP6_ADDR_BLOCK1(ip6_current_dest_addr()), - IP6_ADDR_BLOCK2(ip6_current_dest_addr()), - IP6_ADDR_BLOCK3(ip6_current_dest_addr()), - IP6_ADDR_BLOCK4(ip6_current_dest_addr()), - IP6_ADDR_BLOCK5(ip6_current_dest_addr()), - IP6_ADDR_BLOCK6(ip6_current_dest_addr()), - IP6_ADDR_BLOCK7(ip6_current_dest_addr()), - IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); - - /* transmit pbuf on chosen interface */ - netif->output_ip6(netif, p, ip6_current_dest_addr()); - IP6_STATS_INC(ip6.fw); - IP6_STATS_INC(ip6.xmit); - return; -} -#endif /* LWIP_IPV6_FORWARD */ - - -/** - * This function is called by the network interface device driver when - * an IPv6 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 ip6_forward). - * - * Finally, the packet is sent to the upper layer protocol input function. - * - * @param p the received IPv6 packet (p->payload points to IPv6 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 -ip6_input(struct pbuf *p, struct netif *inp) -{ - struct ip6_hdr *ip6hdr; - struct netif *netif; - u8_t nexth; - u16_t hlen; /* the current header length */ - u8_t i; -#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ - @todo - int check_ip_src=1; -#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ - - IP6_STATS_INC(ip6.recv); - - /* identify the IP header */ - ip6hdr = (struct ip6_hdr *)p->payload; - if (IP6H_V(ip6hdr) != 6) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", - IP6H_V(ip6hdr))); - pbuf_free(p); - IP6_STATS_INC(ip6.err); - IP6_STATS_INC(ip6.drop); - return ERR_OK; - } - - /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ - if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { - if (IP6_HLEN > p->len) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", - IP6_HLEN, p->len)); - } - if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", - IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); - } - /* free (drop) packet pbufs */ - pbuf_free(p); - IP6_STATS_INC(ip6.lenerr); - IP6_STATS_INC(ip6.drop); - return ERR_OK; - } - - /* 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, IP6_HLEN + IP6H_PLEN(ip6hdr)); - - /* copy IP addresses to aligned ip6_addr_t */ - ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); - ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); - - /* current header pointer. */ - ip_data.current_ip6_header = ip6hdr; - - /* In netif, used in case we need to send ICMPv6 packets back. */ - ip_data.current_netif = inp; - - /* match packet against an interface, i.e. is this packet for us? */ - if (ip6_addr_ismulticast(ip6_current_dest_addr())) { - /* Always joined to multicast if-local and link-local all-nodes group. */ - if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || - ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { - netif = inp; - } -#if LWIP_IPV6_MLD - else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { - netif = inp; - } -#else /* LWIP_IPV6_MLD */ - else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { - /* Filter solicited node packets when MLD is not enabled - * (for Neighbor discovery). */ - netif = NULL; - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && - ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { - netif = inp; - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", - netif->name[0], netif->name[1])); - break; - } - } - } -#endif /* LWIP_IPV6_MLD */ - else { - netif = NULL; - } - } - else { - /* 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 { - /* interface is up? */ - if (netif_is_up(netif)) { - /* unicast to this interface address? address configured? */ - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { - /* exit outer loop */ - goto netif_found; - } - } - } - if (ip6_addr_islinklocal(ip6_current_dest_addr())) { - /* Do not match link-local addresses to other netifs. */ - netif = NULL; - break; - } - if (first) { - first = 0; - netif = netif_list; - } else { - netif = netif->next; - } - if (netif == inp) { - netif = netif->next; - } - } while(netif != NULL); -netif_found: - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", - netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); - } - - /* "::" packet source address? (used in duplicate address detection) */ - if (ip6_addr_isany(ip6_current_src_addr()) && - (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { - /* packet source is not valid */ - /* free (drop) packet pbufs */ - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); - pbuf_free(p); - IP6_STATS_INC(ip6.drop); - goto ip6_input_cleanup; - } - - /* packet not for us? */ - if (netif == NULL) { - /* packet not for us, route or discard */ - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); -#if LWIP_IPV6_FORWARD - /* non-multicast packet? */ - if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { - /* try to forward IP packet on (other) interfaces */ - ip6_forward(p, ip6hdr, inp); - } -#endif /* LWIP_IPV6_FORWARD */ - pbuf_free(p); - goto ip6_input_cleanup; - } - - /* current netif pointer. */ - ip_data.current_netif = netif; - - /* Save next header type. */ - nexth = IP6H_NEXTH(ip6hdr); - - /* Init header length. */ - hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; - - /* Move to payload. */ - pbuf_header(p, -IP6_HLEN); - - /* Process known option extension headers, if present. */ - while (nexth != IP6_NEXTH_NONE) - { - switch (nexth) { - case IP6_NEXTH_HOPBYHOP: - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); - /* Get next header type. */ - nexth = *((u8_t *)p->payload); - - /* Get the header length. */ - hlen = 8 * (1 + *((u8_t *)p->payload + 1)); - ip_data.current_ip_header_tot_len += hlen; - - /* Skip over this header. */ - if (hlen > p->len) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", - hlen, p->len)); - /* free (drop) packet pbufs */ - pbuf_free(p); - IP6_STATS_INC(ip6.lenerr); - IP6_STATS_INC(ip6.drop); - goto ip6_input_cleanup; - } - - pbuf_header(p, -hlen); - break; - case IP6_NEXTH_DESTOPTS: - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); - /* Get next header type. */ - nexth = *((u8_t *)p->payload); - - /* Get the header length. */ - hlen = 8 * (1 + *((u8_t *)p->payload + 1)); - ip_data.current_ip_header_tot_len += hlen; - - /* Skip over this header. */ - if (hlen > p->len) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", - hlen, p->len)); - /* free (drop) packet pbufs */ - pbuf_free(p); - IP6_STATS_INC(ip6.lenerr); - IP6_STATS_INC(ip6.drop); - goto ip6_input_cleanup; - } - - pbuf_header(p, -hlen); - break; - case IP6_NEXTH_ROUTING: - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); - /* Get next header type. */ - nexth = *((u8_t *)p->payload); - - /* Get the header length. */ - hlen = 8 * (1 + *((u8_t *)p->payload + 1)); - ip_data.current_ip_header_tot_len += hlen; - - /* Skip over this header. */ - if (hlen > p->len) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", - hlen, p->len)); - /* free (drop) packet pbufs */ - pbuf_free(p); - IP6_STATS_INC(ip6.lenerr); - IP6_STATS_INC(ip6.drop); - goto ip6_input_cleanup; - } - - pbuf_header(p, -hlen); - break; - - case IP6_NEXTH_FRAGMENT: - { - struct ip6_frag_hdr * frag_hdr; - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); - - frag_hdr = (struct ip6_frag_hdr *)p->payload; - - /* Get next header type. */ - nexth = frag_hdr->_nexth; - - /* Fragment Header length. */ - hlen = 8; - ip_data.current_ip_header_tot_len += hlen; - - /* Make sure this header fits in current pbuf. */ - if (hlen > p->len) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", - hlen, p->len)); - /* free (drop) packet pbufs */ - pbuf_free(p); - IP6_FRAG_STATS_INC(ip6_frag.lenerr); - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto ip6_input_cleanup; - } - - /* Offset == 0 and more_fragments == 0? */ - if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) && - ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) { - - /* This is a 1-fragment packet, usually a packet that we have - * already reassembled. Skip this header anc continue. */ - pbuf_header(p, -hlen); - } - else { -#if LWIP_IPV6_REASS - - /* reassemble the packet */ - p = ip6_reass(p); - /* packet not fully reassembled yet? */ - if (p == NULL) { - goto ip6_input_cleanup; - } - - /* Returned p point to IPv6 header. - * Update all our variables and pointers and continue. */ - ip6hdr = (struct ip6_hdr *)p->payload; - nexth = IP6H_NEXTH(ip6hdr); - hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; - pbuf_header(p, -IP6_HLEN); - -#else /* LWIP_IPV6_REASS */ - /* free (drop) packet pbufs */ - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); - pbuf_free(p); - IP6_STATS_INC(ip6.opterr); - IP6_STATS_INC(ip6.drop); - goto ip6_input_cleanup; -#endif /* LWIP_IPV6_REASS */ - } - break; - } - default: - goto options_done; - break; - } - } -options_done: - - /* p points to IPv6 header again. */ - pbuf_header(p, ip_data.current_ip_header_tot_len); - - /* send to upper layers */ - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); - ip6_debug_print(p); - LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); - -#if LWIP_RAW - /* raw input did not eat the packet? */ - if (raw_input(p, inp) == 0) -#endif /* LWIP_RAW */ - { - switch (nexth) { - case IP6_NEXTH_NONE: - pbuf_free(p); - break; -#if LWIP_UDP - case IP6_NEXTH_UDP: -#if LWIP_UDPLITE - case IP6_NEXTH_UDPLITE: -#endif /* LWIP_UDPLITE */ - /* Point to payload. */ - pbuf_header(p, -ip_data.current_ip_header_tot_len); - udp_input(p, inp); - break; -#endif /* LWIP_UDP */ -#if LWIP_TCP - case IP6_NEXTH_TCP: - /* Point to payload. */ - pbuf_header(p, -ip_data.current_ip_header_tot_len); - tcp_input(p, inp); - break; -#endif /* LWIP_TCP */ -#if LWIP_ICMP6 - case IP6_NEXTH_ICMP6: - /* Point to payload. */ - pbuf_header(p, -ip_data.current_ip_header_tot_len); - icmp6_input(p, inp); - break; -#endif /* LWIP_ICMP */ - default: -#if LWIP_ICMP6 - /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ - if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && - (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { - icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); - } -#endif /* LWIP_ICMP */ - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); - pbuf_free(p); - IP6_STATS_INC(ip6.proterr); - IP6_STATS_INC(ip6.drop); - break; - } - } - -ip6_input_cleanup: - ip_data.current_netif = NULL; - ip_data.current_ip6_header = NULL; - ip_data.current_ip_header_tot_len = 0; - ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); - ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); - - return ERR_OK; -} - - -/** - * Sends an IPv6 packet on a network interface. This function constructs - * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is - * used as source (usually during network startup). If the source IPv6 address it - * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network - * interface is filled in as source address. If the destination IPv6 address is - * IP_HDRINCL, p is assumed to already include an IPv6 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 - IPv6 header and p->payload points to that IPv6 header) - * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an - * IP address of the netif is selected and used as source address. - * if src == NULL, IP6_ADDR_ANY is used as source) - * @param dest the destination IPv6 address to send the packet to - * @param hl the Hop Limit value to be set in the IPv6 header - * @param tc the Traffic Class value to be set in the IPv6 header - * @param nexth the Next Header to be set in the IPv6 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 IPv6/LINK headers - * returns errors returned by netif->output - */ -err_t -ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, - u8_t hl, u8_t tc, - u8_t nexth, struct netif *netif) -{ - struct ip6_hdr *ip6hdr; - ip6_addr_t dest_addr; - - /* 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); - - /* Should the IPv6 header be generated or is it already included in p? */ - if (dest != IP_HDRINCL) { - /* generate IPv6 header */ - if (pbuf_header(p, IP6_HLEN)) { - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); - IP6_STATS_INC(ip6.err); - return ERR_BUF; - } - - ip6hdr = (struct ip6_hdr *)p->payload; - LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", - (p->len >= sizeof(struct ip6_hdr))); - - IP6H_HOPLIM_SET(ip6hdr, hl); - IP6H_NEXTH_SET(ip6hdr, nexth); - - /* dest cannot be NULL here */ - ip6_addr_copy(ip6hdr->dest, *dest); - - IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); - IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); - - if (src == NULL) { - src = IP6_ADDR_ANY; - } - else if (ip6_addr_isany(src)) { - src = ip6_select_source_address(netif, dest); - if ((src == NULL) || ip6_addr_isany(src)) { - /* No appropriate source address was found for this packet. */ - LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); - IP6_STATS_INC(ip6.rterr); - return ERR_RTE; - } - } - /* src cannot be NULL here */ - ip6_addr_copy(ip6hdr->src, *src); - - } else { - /* IP header already included in p */ - ip6hdr = (struct ip6_hdr *)p->payload; - ip6_addr_copy(dest_addr, ip6hdr->dest); - dest = &dest_addr; - } - - IP6_STATS_INC(ip6.xmit); - - LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); - ip6_debug_print(p); - -#if ENABLE_LOOPBACK - /* TODO implement loopback for v6 - if (ip6_addr_cmp(dest, netif_ip6_addr(0))) { - return netif_loop_output(netif, p, dest); - }*/ -#endif /* ENABLE_LOOPBACK */ -#if LWIP_IPV6_FRAG - /* don't fragment if interface has mtu set to 0 [loopif] */ - if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { - return ip6_frag(p, netif, dest); - } -#endif /* LWIP_IPV6_FRAG */ - - LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()")); - return netif->output_ip6(netif, p, dest); -} - -/** - * Simple interface to ip6_output_if. It finds the outgoing network - * interface and calls upon ip6_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 - IPv6 header and p->payload points to that IPv6 header) - * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an - * IP address of the netif is selected and used as source address. - * if src == NULL, IP6_ADDR_ANY is used as source) - * @param dest the destination IPv6 address to send the packet to - * @param hl the Hop Limit value to be set in the IPv6 header - * @param tc the Traffic Class value to be set in the IPv6 header - * @param nexth the Next Header to be set in the IPv6 header - * - * @return ERR_RTE if no route is found - * see ip_output_if() for more return values - */ -err_t -ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, - u8_t hl, u8_t tc, u8_t nexth) -{ - struct netif *netif; - struct ip6_hdr *ip6hdr; - ip6_addr_t src_addr, dest_addr; - - /* pbufs passed to IPv6 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 (dest != IP_HDRINCL) { - netif = ip6_route(src, dest); - } else { - /* IP header included in p, read addresses. */ - ip6hdr = (struct ip6_hdr *)p->payload; - ip6_addr_copy(src_addr, ip6hdr->src); - ip6_addr_copy(dest_addr, ip6hdr->dest); - netif = ip6_route(&src_addr, &dest_addr); - } - - if (netif == NULL) { - LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", - IP6_ADDR_BLOCK1(dest), - IP6_ADDR_BLOCK2(dest), - IP6_ADDR_BLOCK3(dest), - IP6_ADDR_BLOCK4(dest), - IP6_ADDR_BLOCK5(dest), - IP6_ADDR_BLOCK6(dest), - IP6_ADDR_BLOCK7(dest), - IP6_ADDR_BLOCK8(dest))); - IP6_STATS_INC(ip6.rterr); - return ERR_RTE; - } - - return ip6_output_if(p, src, dest, hl, tc, nexth, netif); -} - - -#if LWIP_NETIF_HWADDRHINT -/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint - * before calling ip6_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 - IPv6 header and p->payload points to that IPv6 header) - * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an - * IP address of the netif is selected and used as source address. - * if src == NULL, IP6_ADDR_ANY is used as source) - * @param dest the destination IPv6 address to send the packet to - * @param hl the Hop Limit value to be set in the IPv6 header - * @param tc the Traffic Class value to be set in the IPv6 header - * @param nexth the Next Header to be set in the IPv6 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 -ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, - u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) -{ - struct netif *netif; - struct ip6_hdr *ip6hdr; - ip6_addr_t src_addr, dest_addr; - 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 (dest != IP_HDRINCL) { - netif = ip6_route(src, dest); - } else { - /* IP header included in p, read addresses. */ - ip6hdr = (struct ip6_hdr *)p->payload; - ip6_addr_copy(src_addr, ip6hdr->src); - ip6_addr_copy(dest_addr, ip6hdr->dest); - netif = ip6_route(&src_addr, &dest_addr); - } - - if (netif == NULL) { - LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", - IP6_ADDR_BLOCK1(dest), - IP6_ADDR_BLOCK2(dest), - IP6_ADDR_BLOCK3(dest), - IP6_ADDR_BLOCK4(dest), - IP6_ADDR_BLOCK5(dest), - IP6_ADDR_BLOCK6(dest), - IP6_ADDR_BLOCK7(dest), - IP6_ADDR_BLOCK8(dest))); - IP6_STATS_INC(ip6.rterr); - return ERR_RTE; - } - - NETIF_SET_HWADDRHINT(netif, addr_hint); - err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); - NETIF_SET_HWADDRHINT(netif, NULL); - - return err; -} -#endif /* LWIP_NETIF_HWADDRHINT*/ - -#if LWIP_IPV6_MLD -/** - * Add a hop-by-hop options header with a router alert option and padding. - * - * Used by MLD when sending a Multicast listener report/done message. - * - * @param p the packet to which we will prepend the options header - * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) - * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) - * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise - */ -err_t -ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value) -{ - struct ip6_hbh_hdr * hbh_hdr; - - /* Move pointer to make room for hop-by-hop options header. */ - if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { - LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); - IP6_STATS_INC(ip6.err); - return ERR_BUF; - } - - hbh_hdr = (struct ip6_hbh_hdr *)p->payload; - - /* Set fields. */ - hbh_hdr->_nexth = nexth; - hbh_hdr->_hlen = 0; - hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; - hbh_hdr->_ra_opt_dlen = 2; - hbh_hdr->_ra_opt_data = value; - hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; - hbh_hdr->_padn_opt_dlen = 0; - - return ERR_OK; -} -#endif /* LWIP_IPV6_MLD */ - -#if IP6_DEBUG -/* Print an IPv6 header by using LWIP_DEBUGF - * @param p an IPv6 packet, p->payload pointing to the IPv6 header - */ -void -ip6_debug_print(struct pbuf *p) -{ - struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; - - LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); - LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", - IP6H_V(ip6hdr), - IP6H_TC(ip6hdr), - IP6H_FL(ip6hdr))); - LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", - IP6H_PLEN(ip6hdr), - IP6H_NEXTH(ip6hdr), - IP6H_HOPLIM(ip6hdr))); - LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", - IP6_ADDR_BLOCK1(&(ip6hdr->src)), - IP6_ADDR_BLOCK2(&(ip6hdr->src)), - IP6_ADDR_BLOCK3(&(ip6hdr->src)), - IP6_ADDR_BLOCK4(&(ip6hdr->src)))); - LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", - IP6_ADDR_BLOCK5(&(ip6hdr->src)), - IP6_ADDR_BLOCK6(&(ip6hdr->src)), - IP6_ADDR_BLOCK7(&(ip6hdr->src)), - IP6_ADDR_BLOCK8(&(ip6hdr->src)))); - LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); - LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", - IP6_ADDR_BLOCK1(&(ip6hdr->dest)), - IP6_ADDR_BLOCK2(&(ip6hdr->dest)), - IP6_ADDR_BLOCK3(&(ip6hdr->dest)), - IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); - LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", - IP6_ADDR_BLOCK5(&(ip6hdr->dest)), - IP6_ADDR_BLOCK6(&(ip6hdr->dest)), - IP6_ADDR_BLOCK7(&(ip6hdr->dest)), - IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); - LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); -} -#endif /* IP6_DEBUG */ - -#endif /* LWIP_IPV6 */ diff --git a/third_party/lwip/core/ipv6/ip6_addr.c b/third_party/lwip/core/ipv6/ip6_addr.c deleted file mode 100644 index c3f523a..0000000 --- a/third_party/lwip/core/ipv6/ip6_addr.c +++ /dev/null @@ -1,251 +0,0 @@ -/** - * @file - * - * IPv6 addresses. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * Functions for handling IPv6 addresses. - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" - -#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/ip6_addr.h" -#include "lwip/def.h" - -/* used by IP6_ADDR_ANY in ip6_addr.h */ -const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } }; - -#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') -#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) -#endif - -/** - * Check whether "cp" is a valid ascii representation - * of an IPv6 address and convert to a binary address. - * Returns 1 if the address is valid, 0 if not. - * - * @param cp IPv6 address in ascii represenation (e.g. "FF01::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 -ip6addr_aton(const char *cp, ip6_addr_t *addr) -{ - u32_t addr_index, zero_blocks, current_block_index, current_block_value; - const char * s; - - /* Count the number of colons, to count the number of blocks in a "::" sequence - zero_blocks may be 1 even if there are no :: sequences */ - zero_blocks = 8; - for (s = cp; *s != 0; s++) { - if (*s == ':') - zero_blocks--; - else if (!isxdigit(*s)) - break; - } - - /* parse each block */ - addr_index = 0; - current_block_index = 0; - current_block_value = 0; - for (s = cp; *s != 0; s++) { - if (*s == ':') { - if (addr) { - if (current_block_index & 0x1) { - addr->addr[addr_index++] |= current_block_value; - } - else { - addr->addr[addr_index] = current_block_value << 16; - } - } - current_block_index++; - current_block_value = 0; - if (current_block_index > 7) { - /* address too long! */ - return 0; - } if (s[1] == ':') { - s++; - /* "::" found, set zeros */ - while (zero_blocks-- > 0) { - if (current_block_index & 0x1) { - addr_index++; - } - else { - if (addr) { - addr->addr[addr_index] = 0; - } - } - current_block_index++; - } - } - } else if (isxdigit(*s)) { - /* add current digit */ - current_block_value = (current_block_value << 4) + - (isdigit(*s) ? *s - '0' : - 10 + (islower(*s) ? *s - 'a' : *s - 'A')); - } else { - /* unexpected digit, space? CRLF? */ - break; - } - } - - if (addr) { - if (current_block_index & 0x1) { - addr->addr[addr_index++] |= current_block_value; - } - else { - addr->addr[addr_index] = current_block_value << 16; - } - } - - /* convert to network byte order. */ - if (addr) { - for (addr_index = 0; addr_index < 4; addr_index++) { - addr->addr[addr_index] = htonl(addr->addr[addr_index]); - } - } - - if (current_block_index != 7) { - return 0; - } - - return 1; -} - -/** - * Convert numeric IPv6 address into ASCII representation. - * returns ptr to static buffer; not reentrant! - * - * @param addr ip6 address in network order to convert - * @return pointer to a global static (!) buffer that holds the ASCII - * represenation of addr - */ -char * -ip6addr_ntoa(const ip6_addr_t *addr) -{ - static char str[40]; - return ip6addr_ntoa_r(addr, str, 40); -} - -/** - * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. - * - * @param addr ip6 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 * -ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) -{ - u32_t current_block_index, current_block_value; - s32_t zero_flag, i; - - i = 0; - zero_flag = 0; /* used to indicate a zero chain for "::' */ - - for (current_block_index = 0; current_block_index < 8; current_block_index++) { - /* get the current 16-bit block */ - current_block_value = htonl(addr->addr[current_block_index >> 1]); - if ((current_block_index & 0x1) == 0) { - current_block_value = current_block_value >> 16; - } - current_block_value &= 0xffff; - - if (current_block_value == 0) { - /* generate empty block "::" */ - if (!zero_flag) { - if (current_block_index > 0) { - zero_flag = 1; - buf[i++] = ':'; - if (i >= buflen) return NULL; - } - } - } - else { - if (current_block_index > 0) { - buf[i++] = ':'; - if (i >= buflen) return NULL; - } - - if ((current_block_value & 0xf000) == 0) { - zero_flag = 1; - } - else { - buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); - zero_flag = 0; - if (i >= buflen) return NULL; - } - - if (((current_block_value & 0xf00) == 0) && (zero_flag)) { - /* do nothing */ - } - else { - buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); - zero_flag = 0; - if (i >= buflen) return NULL; - } - - if (((current_block_value & 0xf0) == 0) && (zero_flag)) { - /* do nothing */ - } - else { - buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); - zero_flag = 0; - if (i >= buflen) return NULL; - } - - buf[i++] = xchar((current_block_value & 0xf)); - if (i >= buflen) return NULL; - - zero_flag = 0; - } - } - - buf[i] = 0; - - return buf; -} -#endif /* LWIP_IPV6 */ diff --git a/third_party/lwip/core/ipv6/ip6_frag.c b/third_party/lwip/core/ipv6/ip6_frag.c deleted file mode 100644 index b110dd7..0000000 --- a/third_party/lwip/core/ipv6/ip6_frag.c +++ /dev/null @@ -1,698 +0,0 @@ -/** - * @file - * - * IPv6 fragmentation and reassembly. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" -#include "lwip/ip6_frag.h" -#include "lwip/ip6.h" -#include "lwip/icmp6.h" -#include "lwip/nd6.h" -#include "lwip/ip.h" - -#include "lwip/pbuf.h" -#include "lwip/memp.h" -#include "lwip/stats.h" - -#include - -#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ - - -/** 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 IPv6 header, since it replaces - * the Fragment Header in memory in incoming fragments to keep - * track of the various fragments. - */ -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/bpstruct.h" -#endif -PACK_STRUCT_BEGIN -struct ip6_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 - -/* static variables */ -static struct ip6_reassdata *reassdatagrams; -static u16_t ip6_reass_pbufcount; - -/* Forward declarations. */ -static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); -#if IP_REASS_FREE_OLDEST -static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); -#endif /* IP_REASS_FREE_OLDEST */ - -void -ip6_reass_tmr(void) -{ - struct ip6_reassdata *r, *tmp; - - r = reassdatagrams; - while (r != NULL) { - /* Decrement the timer. Once it reaches 0, - * clean up the incomplete fragment assembly */ - if (r->timer > 0) { - r->timer--; - r = r->next; - } else { - /* reassembly timed out */ - tmp = r; - /* get the next pointer before freeing */ - r = r->next; - /* free the helper struct and all enqueued pbufs */ - ip6_reass_free_complete_datagram(tmp); - } - } -} - -/** - * Free a datagram (struct ip6_reassdata) and all its pbufs. - * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), - * sends an ICMP time exceeded packet. - * - * @param ipr datagram to free - */ -static void -ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) -{ - struct ip6_reassdata *prev; - u16_t pbufs_freed = 0; - u8_t clen; - struct pbuf *p; - struct ip6_reass_helper *iprh; - -#if LWIP_ICMP6 - iprh = (struct ip6_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, move back to the original header (we are now pointing to Fragment header). */ - if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { - LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); - } - else { - icmp6_time_exceeded(p, ICMP6_TE_FRAG); - } - clen = pbuf_clen(p); - LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); - pbufs_freed += clen; - pbuf_free(p); - } -#endif /* LWIP_ICMP6 */ - - /* 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 ip6_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 ip6_reassdata from the list and free it. */ - if (ipr == reassdatagrams) { - reassdatagrams = ipr->next; - } else { - prev = reassdatagrams; - while (prev != NULL) { - if (prev->next == ipr) { - break; - } - prev = prev->next; - } - if (prev != NULL) { - prev->next = ipr->next; - } - } - memp_free(MEMP_IP6_REASSDATA, ipr); - - /* Finally, update number of pbufs in reassembly queue */ - LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); - ip6_reass_pbufcount -= pbufs_freed; -} - -#if IP_REASS_FREE_OLDEST -/** - * Free the oldest datagram to make room for enqueueing new fragments. - * The datagram ipr is not freed! - * - * @param ipr ip6_reassdata for the current fragment - * @param pbufs_needed number of pbufs needed to enqueue - * (used for freeing other datagrams if not enough space) - */ -static void -ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) -{ - struct ip6_reassdata *r, *oldest; - - /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, - * but don't free the current datagram! */ - do { - r = oldest = reassdatagrams; - while (r != NULL) { - if (r != ipr) { - if (r->timer <= oldest->timer) { - /* older than the previous oldest */ - oldest = r; - } - } - r = r->next; - } - if (oldest != NULL) { - ip6_reass_free_complete_datagram(oldest); - } - } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL)); -} -#endif /* IP_REASS_FREE_OLDEST */ - -/** - * Reassembles incoming IPv6 fragments into an IPv6 datagram. - * - * @param p points to the IPv6 Fragment Header - * @param len the length of the payload (after Fragment Header) - * @return NULL if reassembly is incomplete, pbuf pointing to - * IPv6 Header if reassembly is complete - */ -struct pbuf * -ip6_reass(struct pbuf *p) -{ - struct ip6_reassdata *ipr, *ipr_prev; - struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; - struct ip6_frag_hdr * frag_hdr; - u16_t offset, len; - u8_t clen, valid = 1; - struct pbuf *q; - - IP6_FRAG_STATS_INC(ip6_frag.recv); - - frag_hdr = (struct ip6_frag_hdr *) p->payload; - - clen = pbuf_clen(p); - - offset = ntohs(frag_hdr->_fragment_offset); - - /* Calculate fragment length from IPv6 payload length. - * Adjust for headers before Fragment Header. - * And finally adjust by Fragment Header length. */ - len = ntohs(ip6_current_header()->_plen); - len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; - len -= IP6_FRAG_HLEN; - - /* 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_prev = NULL; 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 ((frag_hdr->_identification == ipr->identification) && - ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && - ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { - IP6_FRAG_STATS_INC(ip6_frag.cachehit); - break; - } - ipr_prev = ipr; - } - - if (ipr == NULL) { - /* Enqueue a new datagram into the datagram queue */ - ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); - if (ipr == NULL) { -#if IP_REASS_FREE_OLDEST - /* Make room and try again. */ - ip6_reass_remove_oldest_datagram(ipr, clen); - ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); - if (ipr == NULL) -#endif /* IP_REASS_FREE_OLDEST */ - { - IP6_FRAG_STATS_INC(ip6_frag.memerr); - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto nullreturn; - } - } - - memset(ipr, 0, sizeof(struct ip6_reassdata)); - ipr->timer = IP_REASS_MAXAGE; - - /* enqueue the new structure to the front of the list */ - ipr->next = reassdatagrams; - reassdatagrams = ipr; - - /* Use the current IPv6 header for src/dest address reference. - * Eventually, we will replace it when we get the first fragment - * (it might be this one, in any case, it is done later). */ - ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); - - /* copy the fragmented packet id. */ - ipr->identification = frag_hdr->_identification; - - /* copy the nexth field */ - ipr->nexth = frag_hdr->_nexth; - } - - /* Check if we are allowed to enqueue more datagrams. */ - if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { -#if IP_REASS_FREE_OLDEST - ip6_reass_remove_oldest_datagram(ipr, clen); - if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) -#endif /* IP_REASS_FREE_OLDEST */ - { - /* @todo: send ICMPv6 time exceeded here? */ - /* drop this pbuf */ - IP6_FRAG_STATS_INC(ip6_frag.memerr); - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto nullreturn; - } - } - - /* Overwrite Fragment Header with our own helper struct. */ - iprh = (struct ip6_reass_helper *)p->payload; - iprh->next_pbuf = NULL; - iprh->start = (offset & IP6_FRAG_OFFSET_MASK); - iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; - - /* find the right place to insert this pbuf */ - /* 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 ip6_reass_helper*)q->payload; - if (iprh->start < iprh_tmp->start) { -#if IP_REASS_CHECK_OVERLAP - if (iprh->end > iprh_tmp->start) { - /* fragment overlaps with following, throw away */ - IP6_FRAG_STATS_INC(ip6_frag.proterr); - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto nullreturn; - } - if (iprh_prev != NULL) { - if (iprh->start < iprh_prev->end) { - /* fragment overlaps with previous, throw away */ - IP6_FRAG_STATS_INC(ip6_frag.proterr); - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto nullreturn; - } - } -#endif /* IP_REASS_CHECK_OVERLAP */ - /* the new pbuf should be inserted before this */ - iprh->next_pbuf = q; - if (iprh_prev != NULL) { - /* not the fragment with the lowest offset */ - iprh_prev->next_pbuf = p; - } else { - /* fragment with the lowest offset */ - ipr->p = p; - } - break; - } else if(iprh->start == iprh_tmp->start) { - /* received the same datagram twice: no need to keep the datagram */ - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto nullreturn; -#if IP_REASS_CHECK_OVERLAP - } else if(iprh->start < iprh_tmp->end) { - /* overlap: no need to keep the new datagram */ - IP6_FRAG_STATS_INC(ip6_frag.proterr); - IP6_FRAG_STATS_INC(ip6_frag.drop); - goto nullreturn; -#endif /* IP_REASS_CHECK_OVERLAP */ - } else { - /* Check if the fragments received so far have no gaps. */ - 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 = 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 = p; - } - } - - /* 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 */ - ip6_reass_pbufcount += clen; - - /* Remember IPv6 header if this is the first fragment. */ - if (iprh->start == 0) { - ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); - } - - /* If this is the last fragment, calculate total packet length. */ - if ((offset & IP6_FRAG_MORE_FLAG) == 0) { - ipr->datagram_len = iprh->end; - } - - /* Additional validity tests: we have received first and last fragment. */ - iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; - if (iprh_tmp->start != 0) { - valid = 0; - } - if (ipr->datagram_len == 0) { - valid = 0; - } - - /* Final validity test: no gaps between current and last fragment. */ - iprh_prev = iprh; - q = iprh->next_pbuf; - while ((q != NULL) && valid) { - iprh = (struct ip6_reass_helper*)q->payload; - if (iprh_prev->end != iprh->start) { - valid = 0; - break; - } - iprh_prev = iprh; - q = iprh->next_pbuf; - } - - if (valid) { - /* All fragments have been received */ - - /* chain together the pbufs contained within the ip6_reassdata list. */ - iprh = (struct ip6_reass_helper*) ipr->p->payload; - while(iprh != NULL) { - - if (iprh->next_pbuf != NULL) { - /* Save next helper struct (will be hidden in next step). */ - iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; - - /* hide the fragment header for every succeding fragment */ - pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); - pbuf_cat(ipr->p, iprh->next_pbuf); - } - else { - iprh_tmp = NULL; - } - - iprh = iprh_tmp; - } - - /* Adjust datagram length by adding header lengths. */ - ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) - + IP6_FRAG_HLEN - - IP6_HLEN ; - - /* Set payload length in ip header. */ - ipr->iphdr->_plen = htons(ipr->datagram_len); - - /* Get the furst pbuf. */ - p = ipr->p; - - /* Restore Fragment Header in first pbuf. Mark as "single fragment" - * packet. Restore nexth. */ - frag_hdr = (struct ip6_frag_hdr *) p->payload; - frag_hdr->_nexth = ipr->nexth; - frag_hdr->reserved = 0; - frag_hdr->_fragment_offset = 0; - frag_hdr->_identification = 0; - - /* release the sources allocate for the fragment queue entry */ - 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", ipr_prev != NULL); - ipr_prev->next = ipr->next; - } - memp_free(MEMP_IP6_REASSDATA, ipr); - - /* adjust the number of pbufs currently queued for reassembly. */ - ip6_reass_pbufcount -= pbuf_clen(p); - - /* Move pbuf back to IPv6 header. */ - if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { - LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); - pbuf_free(p); - return NULL; - } - - /* Return the pbuf chain */ - return p; - } - /* the datagram is not (yet?) reassembled completely */ - return NULL; - -nullreturn: - pbuf_free(p); - return NULL; -} - -#endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */ - -#if LWIP_IPV6 && LWIP_IPV6_FRAG - -/** Allocate a new struct pbuf_custom_ref */ -static struct pbuf_custom_ref* -ip6_frag_alloc_pbuf_custom_ref(void) -{ - return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); -} - -/** Free a struct pbuf_custom_ref */ -static void -ip6_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 -ip6_frag_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); - } - ip6_frag_free_pbuf_custom_ref(pcr); -} - -/** - * Fragment an IPv6 datagram if too large for the netif or path MTU. - * - * Chop the datagram in MTU sized chunks and send them in order - * by pointing PBUF_REFs into p - * - * @param p ipv6 packet to send - * @param netif the netif on which to send - * @param dest destination ipv6 address to which to send - * - * @return ERR_OK if sent successfully, err_t otherwise - */ -err_t -ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest) -{ - struct ip6_hdr *original_ip6hdr; - struct ip6_hdr *ip6hdr; - struct ip6_frag_hdr * frag_hdr; - struct pbuf *rambuf; - struct pbuf *newpbuf; - static u32_t identification; - u16_t nfb; - u16_t left, cop; - u16_t mtu; - u16_t fragment_offset = 0; - u16_t last; - u16_t poff = IP6_HLEN; - u16_t newpbuflen = 0; - u16_t left_to_copy; - - identification++; - - original_ip6hdr = (struct ip6_hdr *)p->payload; - - mtu = nd6_get_destination_mtu(dest, netif); - - /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */ - left = p->tot_len - IP6_HLEN; - - nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; - - while (left) { - last = (left <= nfb); - - /* Fill this fragment */ - cop = last ? left : nfb; - - /* When not using a static buffer, create a chain of pbufs. - * The first will be a PBUF_RAM holding the link, IPv6, and Fragment 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, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM); - if (rambuf == NULL) { - IP6_FRAG_STATS_INC(ip6_frag.memerr); - return ERR_MEM; - } - LWIP_ASSERT("this needs a pbuf in one piece!", - (p->len >= (IP6_HLEN + IP6_FRAG_HLEN))); - SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); - ip6hdr = (struct ip6_hdr *)rambuf->payload; - frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); - - /* Can just adjust p directly for needed offset. */ - p->payload = (u8_t *)p->payload + poff; - p->len -= poff; - p->tot_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 = ip6_frag_alloc_pbuf_custom_ref(); - if (pcr == NULL) { - pbuf_free(rambuf); - IP6_FRAG_STATS_INC(ip6_frag.memerr); - 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) { - ip6_frag_free_pbuf_custom_ref(pcr); - pbuf_free(rambuf); - IP6_FRAG_STATS_INC(ip6_frag.memerr); - return ERR_MEM; - } - pbuf_ref(p); - pcr->original = p; - pcr->pc.custom_free_function = ip6_frag_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; - - /* Set headers */ - frag_hdr->_nexth = original_ip6hdr->_nexth; - frag_hdr->reserved = 0; - frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); - frag_hdr->_identification = htonl(identification); - - IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); - IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); - - /* No need for separate header pbuf - we allowed room for it in rambuf - * when allocated. - */ - IP6_FRAG_STATS_INC(ip6_frag.xmit); - netif->output_ip6(netif, rambuf, dest); - - /* 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); - left -= cop; - fragment_offset += cop; - } - return ERR_OK; -} - -#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/third_party/lwip/core/ipv6/mld6.c b/third_party/lwip/core/ipv6/mld6.c deleted file mode 100644 index ce6e732..0000000 --- a/third_party/lwip/core/ipv6/mld6.c +++ /dev/null @@ -1,581 +0,0 @@ -/** - * @file - * - * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. - * No support for MLDv2. - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -/* Based on igmp.c implementation of igmp v2 protocol */ - -#include "lwip/opt.h" - -#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/mld6.h" -#include "lwip/icmp6.h" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/ip.h" -#include "lwip/inet_chksum.h" -#include "lwip/pbuf.h" -#include "lwip/netif.h" -#include "lwip/memp.h" -#include "lwip/stats.h" - -#include - - -/* - * MLD constants - */ -#define MLD6_HL 1 -#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) - -#define MLD6_GROUP_NON_MEMBER 0 -#define MLD6_GROUP_DELAYING_MEMBER 1 -#define MLD6_GROUP_IDLE_MEMBER 2 - - -/* The list of joined groups. */ -static struct mld_group* mld_group_list; - - -/* Forward declarations. */ -static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr); -static err_t mld6_free_group(struct mld_group *group); -static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); -static void mld6_send(struct mld_group *group, u8_t type); - - -/** - * Stop MLD processing on interface - * - * @param netif network interface on which stop MLD processing - */ -err_t -mld6_stop(struct netif *netif) -{ - struct mld_group *group = mld_group_list; - struct mld_group *prev = NULL; - struct mld_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 == mld_group_list) { - mld_group_list = next; - } - /* is there a "previous" group defined? */ - if (prev != NULL) { - prev->next = next; - } - /* disable the group at the MAC level */ - if (netif->mld_mac_filter != NULL) { - netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER); - } - /* free group */ - memp_free(MEMP_MLD6_GROUP, group); - } else { - /* change the "previous" */ - prev = group; - } - /* move to "next" */ - group = next; - } - return ERR_OK; -} - -/** - * Report MLD memberships for this interface - * - * @param netif network interface on which report MLD memberships - */ -void -mld6_report_groups(struct netif *netif) -{ - struct mld_group *group = mld_group_list; - - while (group != NULL) { - if (group->netif == netif) { - mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); - } - group = group->next; - } -} - -/** - * Search for a group that is joined on a netif - * - * @param ifp the network interface for which to look - * @param addr the group ipv6 address to search for - * @return a struct mld_group* if the group has been found, - * NULL if the group wasn't found. - */ -struct mld_group * -mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr) -{ - struct mld_group *group = mld_group_list; - - while (group != NULL) { - if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) { - return group; - } - group = group->next; - } - - return NULL; -} - - -/** - * create a new group - * - * @param ifp the network interface for which to create - * @param addr the new group ipv6 - * @return a struct mld_group*, - * NULL on memory error. - */ -static struct mld_group * -mld6_new_group(struct netif *ifp, ip6_addr_t *addr) -{ - struct mld_group *group; - - group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); - if (group != NULL) { - group->netif = ifp; - ip6_addr_set(&(group->group_address), addr); - group->timer = 0; /* Not running */ - group->group_state = MLD6_GROUP_IDLE_MEMBER; - group->last_reporter_flag = 0; - group->use = 0; - group->next = mld_group_list; - - mld_group_list = group; - } - - return group; -} - -/** - * Remove a group in the mld_group_list and free - * - * @param group the group to remove - * @return ERR_OK if group was removed from the list, an err_t otherwise - */ -static err_t -mld6_free_group(struct mld_group *group) -{ - err_t err = ERR_OK; - - /* Is it the first group? */ - if (mld_group_list == group) { - mld_group_list = group->next; - } else { - /* look for group further down the list */ - struct mld_group *tmpGroup; - for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { - if (tmpGroup->next == group) { - tmpGroup->next = group->next; - break; - } - } - /* Group not find group */ - if (tmpGroup == NULL) - err = ERR_ARG; - } - /* free group */ - memp_free(MEMP_MLD6_GROUP, group); - - return err; -} - - -/** - * Process an input MLD message. Called by icmp6_input. - * - * @param p the mld packet, p->payload pointing to the icmpv6 header - * @param inp the netif on which this packet was received - */ -void -mld6_input(struct pbuf *p, struct netif *inp) -{ - struct mld_header * mld_hdr; - struct mld_group* group; - - MLD6_STATS_INC(mld6.recv); - - /* Check that mld header fits in packet. */ - if (p->len < sizeof(struct mld_header)) { - /* TODO debug message */ - pbuf_free(p); - MLD6_STATS_INC(mld6.lenerr); - MLD6_STATS_INC(mld6.drop); - return; - } - - mld_hdr = (struct mld_header *)p->payload; - - switch (mld_hdr->type) { - case ICMP6_TYPE_MLQ: /* Multicast listener query. */ - { - /* Is it a general query? */ - if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && - ip6_addr_isany(&(mld_hdr->multicast_address))) { - MLD6_STATS_INC(mld6.rx_general); - /* Report all groups, except all nodes group, and if-local groups. */ - group = mld_group_list; - while (group != NULL) { - if ((group->netif == inp) && - (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && - (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { - mld6_delayed_report(group, mld_hdr->max_resp_delay); - } - group = group->next; - } - } - else { - /* Have we joined this group? - * We use IP6 destination address to have a memory aligned copy. - * mld_hdr->multicast_address should be the same. */ - MLD6_STATS_INC(mld6.rx_group); - group = mld6_lookfor_group(inp, ip6_current_dest_addr()); - if (group != NULL) { - /* Schedule a report. */ - mld6_delayed_report(group, mld_hdr->max_resp_delay); - } - } - break; /* ICMP6_TYPE_MLQ */ - } - case ICMP6_TYPE_MLR: /* Multicast listener report. */ - { - /* Have we joined this group? - * We use IP6 destination address to have a memory aligned copy. - * mld_hdr->multicast_address should be the same. */ - MLD6_STATS_INC(mld6.rx_report); - group = mld6_lookfor_group(inp, ip6_current_dest_addr()); - if (group != NULL) { - /* If we are waiting to report, cancel it. */ - if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { - group->timer = 0; /* stopped */ - group->group_state = MLD6_GROUP_IDLE_MEMBER; - group->last_reporter_flag = 0; - } - } - break; /* ICMP6_TYPE_MLR */ - } - case ICMP6_TYPE_MLD: /* Multicast listener done. */ - { - /* Do nothing, router will query us. */ - break; /* ICMP6_TYPE_MLD */ - } - default: - MLD6_STATS_INC(mld6.proterr); - MLD6_STATS_INC(mld6.drop); - break; - } - - pbuf_free(p); -} - -/** - * Join a group on a network interface. - * - * @param srcaddr ipv6 address of the network interface which should - * join a new group. If IP6_ADDR_ANY, join on all netifs - * @param groupaddr the ipv6 address of the group to join - * @return ERR_OK if group was joined on the netif(s), an err_t otherwise - */ -err_t -mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) -{ - err_t err = ERR_VAL; /* no matching interface */ - struct mld_group *group; - struct netif *netif; - u8_t match; - u8_t i; - - /* loop through netif's */ - netif = netif_list; - while (netif != NULL) { - /* Should we join this interface ? */ - match = 0; - if (ip6_addr_isany(srcaddr)) { - match = 1; - } - else { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { - match = 1; - break; - } - } - } - if (match) { - /* find group or create a new one if not found */ - group = mld6_lookfor_group(netif, groupaddr); - - if (group == NULL) { - /* Joining a new group. Create a new group entry. */ - group = mld6_new_group(netif, groupaddr); - if (group == NULL) { - return ERR_MEM; - } - - /* Activate this address on the MAC layer. */ - if (netif->mld_mac_filter != NULL) { - netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER); - } - - /* Report our membership. */ - MLD6_STATS_INC(mld6.tx_report); - mld6_send(group, ICMP6_TYPE_MLR); - mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); - } - - /* Increment group use */ - group->use++; - err = ERR_OK; - } - - /* proceed to next network interface */ - netif = netif->next; - } - - return err; -} - -/** - * Leave a group on a network interface. - * - * @param srcaddr ipv6 address of the network interface which should - * leave the group. If IP6_ISANY, leave on all netifs - * @param groupaddr the ipv6 address of the group to leave - * @return ERR_OK if group was left on the netif(s), an err_t otherwise - */ -err_t -mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) -{ - err_t err = ERR_VAL; /* no matching interface */ - struct mld_group *group; - struct netif *netif; - u8_t match; - u8_t i; - - /* loop through netif's */ - netif = netif_list; - while (netif != NULL) { - /* Should we leave this interface ? */ - match = 0; - if (ip6_addr_isany(srcaddr)) { - match = 1; - } - else { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { - match = 1; - break; - } - } - } - if (match) { - /* find group */ - group = mld6_lookfor_group(netif, groupaddr); - - if (group != NULL) { - /* Leave 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) { - MLD6_STATS_INC(mld6.tx_leave); - mld6_send(group, ICMP6_TYPE_MLD); - } - - /* Disable the group at the MAC level */ - if (netif->mld_mac_filter != NULL) { - netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER); - } - - /* Free the group */ - mld6_free_group(group); - } else { - /* Decrement group use */ - group->use--; - } - /* Leave on this interface */ - err = ERR_OK; - } - } - /* proceed to next network interface */ - netif = netif->next; - } - - return err; -} - - -/** - * Periodic timer for mld processing. Must be called every - * MLD6_TMR_INTERVAL milliseconds (100). - * - * When a delaying member expires, a membership report is sent. - */ -void -mld6_tmr(void) -{ - struct mld_group *group = mld_group_list; - - while (group != NULL) { - if (group->timer > 0) { - group->timer--; - if (group->timer == 0) { - /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ - if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { - MLD6_STATS_INC(mld6.tx_report); - mld6_send(group, ICMP6_TYPE_MLR); - group->group_state = MLD6_GROUP_IDLE_MEMBER; - } - } - } - group = group->next; - } -} - -/** - * Schedule a delayed membership report for a group - * - * @param group the mld_group for which "delaying" membership report - * should be sent - * @param maxresp the max resp delay provided in the query - */ -static void -mld6_delayed_report(struct mld_group *group, u16_t maxresp) -{ - /* Convert maxresp from milliseconds to tmr ticks */ - maxresp = maxresp / MLD6_TMR_INTERVAL; - if (maxresp == 0) { - maxresp = 1; - } - -#ifdef LWIP_RAND - /* Randomize maxresp. (if LWIP_RAND is supported) */ - maxresp = (LWIP_RAND() % (maxresp - 1)) + 1; -#endif /* LWIP_RAND */ - - /* Apply timer value if no report has been scheduled already. */ - if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || - ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && - ((group->timer == 0) || (maxresp < group->timer)))) { - group->timer = maxresp; - group->group_state = MLD6_GROUP_DELAYING_MEMBER; - } -} - -/** - * Send a MLD message (report or done). - * - * An IPv6 hop-by-hop options header with a router alert option - * is prepended. - * - * @param group the group to report or quit - * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) - */ -static void -mld6_send(struct mld_group *group, u8_t type) -{ - struct mld_header * mld_hdr; - struct pbuf * p; - ip6_addr_t * src_addr; - - /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ - p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); - if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) { - /* We couldn't allocate a suitable pbuf. drop it. */ - if (p != NULL) { - pbuf_free(p); - } - MLD6_STATS_INC(mld6.memerr); - return; - } - - /* Move to make room for Hop-by-hop options header. */ - if (pbuf_header(p, -IP6_HBH_HLEN)) { - pbuf_free(p); - MLD6_STATS_INC(mld6.lenerr); - return; - } - - /* Select our source address. */ - if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) { - /* This is a special case, when we are performing duplicate address detection. - * We must join the multicast group, but we don't have a valid address yet. */ - src_addr = IP6_ADDR_ANY; - } else { - /* Use link-local address as source address. */ - src_addr = netif_ip6_addr(group->netif, 0); - } - - /* MLD message header pointer. */ - mld_hdr = (struct mld_header *)p->payload; - - /* Set fields. */ - mld_hdr->type = type; - mld_hdr->code = 0; - mld_hdr->chksum = 0; - mld_hdr->max_resp_delay = 0; - mld_hdr->reserved = 0; - ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); - - mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, - src_addr, &(group->group_address)); - - /* Add hop-by-hop headers options: router alert with MLD value. */ - ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); - - /* Send the packet out. */ - MLD6_STATS_INC(mld6.xmit); - ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), - MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif); - pbuf_free(p); -} - - - -#endif /* LWIP_IPV6 */ diff --git a/third_party/lwip/core/ipv6/nd6.c b/third_party/lwip/core/ipv6/nd6.c deleted file mode 100644 index d5855fb..0000000 --- a/third_party/lwip/core/ipv6/nd6.c +++ /dev/null @@ -1,1786 +0,0 @@ -/** - * @file - * - * Neighbor discovery and stateless address autoconfiguration for IPv6. - * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 - * (Address autoconfiguration). - */ - -/* - * Copyright (c) 2010 Inico 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. 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: Ivan Delamer - * - * - * Please coordinate changes and requests with Ivan Delamer - * - */ - -#include "lwip/opt.h" - -#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ - -#include "lwip/nd6.h" -#include "lwip/pbuf.h" -#include "lwip/mem.h" -#include "lwip/memp.h" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/inet_chksum.h" -#include "lwip/netif.h" -#include "lwip/icmp6.h" -#include "lwip/mld6.h" -#include "lwip/ip.h" -#include "lwip/stats.h" - -#include - - -/* Router tables. */ -struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; -struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; -struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; -struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; - -/* Default values, can be updated by a RA message. */ -u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; -u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */ - -/* Index for cache entries. */ -static u8_t nd6_cached_neighbor_index; -static u8_t nd6_cached_destination_index; - -/* Multicast address holder. */ -static ip6_addr_t multicast_address; - -/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ -static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; - -/* Forward declarations. */ -static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr); -static s8_t nd6_new_neighbor_cache_entry(void); -static void nd6_free_neighbor_cache_entry(s8_t i); -static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr); -static s8_t nd6_new_destination_cache_entry(void); -static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif); -static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif); -static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif); -static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); -static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); - -#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 -#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 -static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); -static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); -#if LWIP_IPV6_SEND_ROUTER_SOLICIT -static void nd6_send_rs(struct netif * netif); -#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ - -#if LWIP_ND6_QUEUEING -static void nd6_free_q(struct nd6_q_entry *q); -#else /* LWIP_ND6_QUEUEING */ -#define nd6_free_q(q) pbuf_free(q) -#endif /* LWIP_ND6_QUEUEING */ -static void nd6_send_q(s8_t i); - - -/** - * Process an incoming neighbor discovery message - * - * @param p the nd packet, p->payload pointing to the icmpv6 header - * @param inp the netif on which this packet was received - */ -void -nd6_input(struct pbuf *p, struct netif *inp) -{ - u8_t msg_type; - s8_t i; - - ND6_STATS_INC(nd6.recv); - - msg_type = *((u8_t *)p->payload); - switch (msg_type) { - case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ - { - struct na_header * na_hdr; - struct lladdr_option * lladdr_opt; - - /* Check that na header fits in packet. */ - if (p->len < (sizeof(struct na_header))) { - /* TODO debug message */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - na_hdr = (struct na_header *)p->payload; - - /* Unsolicited NA?*/ - if (ip6_addr_ismulticast(ip6_current_dest_addr())) { - /* This is an unsolicited NA. - * link-layer changed? - * part of DAD mechanism? */ - - /* Check that link-layer address option also fits in packet. */ - if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { - /* TODO debug message */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); - - /* Override ip6_current_dest_addr() so that we have an aligned copy. */ - ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); - -#if LWIP_IPV6_DUP_DETECT_ATTEMPTS - /* If the target address matches this netif, it is a DAD response. */ - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { - /* We are using a duplicate address. */ - netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); - -#if LWIP_IPV6_MLD - /* Leave solicited node multicast group. */ - ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]); - mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address); -#endif /* LWIP_IPV6_MLD */ - - - - -#if LWIP_IPV6_AUTOCONFIG - /* Check to see if this address was autoconfigured. */ - if (!ip6_addr_islinklocal(ip6_current_dest_addr())) { - i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); - if (i >= 0) { - /* Mark this prefix as duplicate, so that we don't use it - * to generate this address again. */ - prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; - } - } -#endif /* LWIP_IPV6_AUTOCONFIG */ - - pbuf_free(p); - return; - } - } -#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ - - /* This is an unsolicited NA, most likely there was a LLADDR change. */ - i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); - if (i >= 0) { - if (na_hdr->flags & ND6_FLAG_OVERRIDE) { - MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - } - } - } - else { - /* This is a solicited NA. - * neighbor address resolution response? - * neighbor unreachability detection response? */ - - /* Override ip6_current_dest_addr() so that we have an aligned copy. */ - ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); - - /* Find the cache entry corresponding to this na. */ - i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); - if (i < 0) { - /* We no longer care about this target address. drop it. */ - pbuf_free(p); - return; - } - - /* Update cache entry. */ - neighbor_cache[i].netif = inp; - neighbor_cache[i].counter.reachable_time = reachable_time; - if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || - (neighbor_cache[i].state == ND6_INCOMPLETE)) { - /* Check that link-layer address option also fits in packet. */ - if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { - /* TODO debug message */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); - - MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - } - neighbor_cache[i].state = ND6_REACHABLE; - - /* Send queued packets, if any. */ - if (neighbor_cache[i].q != NULL) { - nd6_send_q(i); - } - } - - break; /* ICMP6_TYPE_NA */ - } - case ICMP6_TYPE_NS: /* Neighbor solicitation. */ - { - struct ns_header * ns_hdr; - struct lladdr_option * lladdr_opt; - u8_t accepted; - - /* Check that ns header fits in packet. */ - if (p->len < sizeof(struct ns_header)) { - /* TODO debug message */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - ns_hdr = (struct ns_header *)p->payload; - - /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ - lladdr_opt = NULL; - if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) { - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); - } - - /* Check if the target address is configured on the receiving netif. */ - accepted = 0; - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { - if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || - (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && - ip6_addr_isany(ip6_current_src_addr()))) && - ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { - accepted = 1; - break; - } - } - - /* NS not for us? */ - if (!accepted) { - pbuf_free(p); - return; - } - - /* Check for ANY address in src (DAD algorithm). */ - if (ip6_addr_isany(ip6_current_src_addr())) { - /* Sender is validating this address. */ - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { - if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { - /* Send a NA back so that the sender does not use this address. */ - nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); - if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { - /* We shouldn't use this address either. */ - netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); - } - } - } - } - else { - /* Sender is trying to resolve our address. */ - /* Verify that they included their own link-layer address. */ - if (lladdr_opt == NULL) { - /* Not a valid message. */ - pbuf_free(p); - ND6_STATS_INC(nd6.proterr); - ND6_STATS_INC(nd6.drop); - return; - } - - i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); - if ( i>= 0) { - /* We already have a record for the solicitor. */ - if (neighbor_cache[i].state == ND6_INCOMPLETE) { - neighbor_cache[i].netif = inp; - MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - - /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ - neighbor_cache[i].state = ND6_DELAY; - neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; - } - } - else - { - /* Add their IPv6 address and link-layer address to neighbor cache. - * We will need it at least to send a unicast NA message, but most - * likely we will also be communicating with this node soon. */ - i = nd6_new_neighbor_cache_entry(); - if (i < 0) { - /* We couldn't assign a cache entry for this neighbor. - * we won't be able to reply. drop it. */ - pbuf_free(p); - ND6_STATS_INC(nd6.memerr); - return; - } - neighbor_cache[i].netif = inp; - MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); - - /* Receiving a message does not prove reachability: only in one direction. - * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ - neighbor_cache[i].state = ND6_DELAY; - neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; - } - - /* Override ip6_current_dest_addr() so that we have an aligned copy. */ - ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address)); - - /* Send back a NA for us. Allocate the reply pbuf. */ - nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); - } - - break; /* ICMP6_TYPE_NS */ - } - case ICMP6_TYPE_RA: /* Router Advertisement. */ - { - struct ra_header * ra_hdr; - u8_t * buffer; /* Used to copy options. */ - u16_t offset; - - /* Check that RA header fits in packet. */ - if (p->len < sizeof(struct ra_header)) { - /* TODO debug message */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - ra_hdr = (struct ra_header *)p->payload; - - /* If we are sending RS messages, stop. */ -#if LWIP_IPV6_SEND_ROUTER_SOLICIT - inp->rs_count = 0; -#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ - - /* Get the matching default router entry. */ - i = nd6_get_router(ip6_current_src_addr(), inp); - if (i < 0) { - /* Create a new router entry. */ - i = nd6_new_router(ip6_current_src_addr(), inp); - } - - if (i < 0) { - /* Could not create a new router entry. */ - pbuf_free(p); - ND6_STATS_INC(nd6.memerr); - return; - } - - /* Re-set invalidation timer. */ - default_router_list[i].invalidation_timer = ra_hdr->router_lifetime; - - /* Re-set default timer values. */ -#if LWIP_ND6_ALLOW_RA_UPDATES - if (ra_hdr->retrans_timer > 0) { - retrans_timer = ra_hdr->retrans_timer; - } - if (ra_hdr->reachable_time > 0) { - reachable_time = ra_hdr->reachable_time; - } -#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ - - /* TODO set default hop limit... */ - /* ra_hdr->current_hop_limit;*/ - - /* Update flags in local entry (incl. preference). */ - default_router_list[i].flags = ra_hdr->flags; - - /* Offset to options. */ - offset = sizeof(struct ra_header); - - /* Process each option. */ - while ((p->tot_len - offset) > 0) { - if (p->len == p->tot_len) { - /* no need to copy from contiguous pbuf */ - buffer = &((u8_t*)p->payload)[offset]; - } else { - buffer = nd6_ra_buffer; - pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset); - } - switch (buffer[0]) { - case ND6_OPTION_TYPE_SOURCE_LLADDR: - { - struct lladdr_option * lladdr_opt; - lladdr_opt = (struct lladdr_option *)buffer; - if ((default_router_list[i].neighbor_entry != NULL) && - (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { - SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); - default_router_list[i].neighbor_entry->state = ND6_REACHABLE; - default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; - } - break; - } - case ND6_OPTION_TYPE_MTU: - { - struct mtu_option * mtu_opt; - mtu_opt = (struct mtu_option *)buffer; - if (mtu_opt->mtu >= 1280) { -#if LWIP_ND6_ALLOW_RA_UPDATES - inp->mtu = mtu_opt->mtu; -#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ - } - break; - } - case ND6_OPTION_TYPE_PREFIX_INFO: - { - struct prefix_option * prefix_opt; - prefix_opt = (struct prefix_option *)buffer; - - if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) { - /* Add to on-link prefix list. */ - - /* Get a memory-aligned copy of the prefix. */ - ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix)); - - /* find cache entry for this prefix. */ - i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); - if (i < 0) { - /* Create a new cache entry. */ - i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp); - } - if (i >= 0) { - prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime; - -#if LWIP_IPV6_AUTOCONFIG - if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { - /* Mark prefix as autonomous, so that address autoconfiguration can take place. - * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ - prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; - } -#endif /* LWIP_IPV6_AUTOCONFIG */ - } - } - - break; - } - case ND6_OPTION_TYPE_ROUTE_INFO: - { - /* TODO implement preferred routes. - struct route_option * route_opt; - route_opt = (struct route_option *)buffer;*/ - - break; - } - default: - /* Unrecognized option, abort. */ - ND6_STATS_INC(nd6.proterr); - break; - } - offset += 8 * ((u16_t)buffer[1]); - } - - break; /* ICMP6_TYPE_RA */ - } - case ICMP6_TYPE_RD: /* Redirect */ - { - struct redirect_header * redir_hdr; - struct lladdr_option * lladdr_opt; - - /* Check that Redir header fits in packet. */ - if (p->len < sizeof(struct redirect_header)) { - /* TODO debug message */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - redir_hdr = (struct redirect_header *)p->payload; - - lladdr_opt = NULL; - if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) { - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); - } - - /* Copy original destination address to current source address, to have an aligned copy. */ - ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address)); - - /* Find dest address in cache */ - i = nd6_find_destination_cache_entry(ip6_current_src_addr()); - if (i < 0) { - /* Destination not in cache, drop packet. */ - pbuf_free(p); - return; - } - - /* Set the new target address. */ - ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); - - /* If Link-layer address of other router is given, try to add to neighbor cache. */ - if (lladdr_opt != NULL) { - if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { - /* Copy target address to current source address, to have an aligned copy. */ - ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address)); - - i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); - if (i < 0) { - i = nd6_new_neighbor_cache_entry(); - if (i >= 0) { - neighbor_cache[i].netif = inp; - MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); - - /* Receiving a message does not prove reachability: only in one direction. - * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ - neighbor_cache[i].state = ND6_DELAY; - neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; - } - } - if (i >= 0) { - if (neighbor_cache[i].state == ND6_INCOMPLETE) { - MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - /* Receiving a message does not prove reachability: only in one direction. - * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ - neighbor_cache[i].state = ND6_DELAY; - neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; - } - } - } - } - break; /* ICMP6_TYPE_RD */ - } - case ICMP6_TYPE_PTB: /* Packet too big */ - { - struct icmp6_hdr *icmp6hdr; /* Packet too big message */ - struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */ - - /* Check that ICMPv6 header + IPv6 header fit in payload */ - if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { - /* drop short packets */ - pbuf_free(p); - ND6_STATS_INC(nd6.lenerr); - ND6_STATS_INC(nd6.drop); - return; - } - - icmp6hdr = (struct icmp6_hdr *)p->payload; - ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); - - /* Copy original destination address to current source address, to have an aligned copy. */ - ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest)); - - /* Look for entry in destination cache. */ - i = nd6_find_destination_cache_entry(ip6_current_src_addr()); - if (i < 0) { - /* Destination not in cache, drop packet. */ - pbuf_free(p); - return; - } - - /* Change the Path MTU. */ - destination_cache[i].pmtu = icmp6hdr->data; - - break; /* ICMP6_TYPE_PTB */ - } - - default: - ND6_STATS_INC(nd6.proterr); - ND6_STATS_INC(nd6.drop); - break; /* default */ - } - - pbuf_free(p); -} - - -/** - * Periodic timer for Neighbor discovery functions: - * - * - Update neighbor reachability states - * - Update destination cache entries age - * - Update invalidation timers of default routers and on-link prefixes - * - Perform duplicate address detection (DAD) for our addresses - * - Send router solicitations - */ -void -nd6_tmr(void) -{ - s8_t i, j; - struct netif * netif; - - /* Process neighbor entries. */ - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - switch (neighbor_cache[i].state) { - case ND6_INCOMPLETE: - if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { - /* Retries exceeded. */ - nd6_free_neighbor_cache_entry(i); - } - else { - /* Send a NS for this entry. */ - neighbor_cache[i].counter.probes_sent++; - nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST); - } - break; - case ND6_REACHABLE: - /* Send queued packets, if any are left. Should have been sent already. */ - if (neighbor_cache[i].q != NULL) { - nd6_send_q(i); - } - if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { - /* Change to stale state. */ - neighbor_cache[i].state = ND6_STALE; - neighbor_cache[i].counter.stale_time = 0; - } - else { - neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; - } - break; - case ND6_STALE: - neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL; - break; - case ND6_DELAY: - if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) { - /* Change to PROBE state. */ - neighbor_cache[i].state = ND6_PROBE; - neighbor_cache[i].counter.probes_sent = 0; - } - else { - neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL; - } - break; - case ND6_PROBE: - if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { - /* Retries exceeded. */ - nd6_free_neighbor_cache_entry(i); - } - else { - /* Send a NS for this entry. */ - neighbor_cache[i].counter.probes_sent++; - nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0); - } - break; - case ND6_NO_ENTRY: - default: - /* Do nothing. */ - break; - } - } - - /* Process destination entries. */ - for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { - destination_cache[i].age++; - } - - /* Process router entries. */ - for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { - if (default_router_list[i].neighbor_entry != NULL) { - /* Active entry. */ - if (default_router_list[i].invalidation_timer > 0) { - default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; - } - if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { - /* Less than 1 second remainig. Clear this entry. */ - default_router_list[i].neighbor_entry->isrouter = 0; - default_router_list[i].neighbor_entry = NULL; - default_router_list[i].invalidation_timer = 0; - default_router_list[i].flags = 0; - } - } - } - - /* Process prefix entries. */ - for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { - if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { - prefix_list[i].invalidation_timer = 0; - } - if ((prefix_list[i].invalidation_timer > 0) && - (prefix_list[i].netif != NULL)) { - prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; - -#if LWIP_IPV6_AUTOCONFIG - /* Initiate address autoconfiguration for this prefix, if conditions are met. */ - if (prefix_list[i].netif->ip6_autoconfig_enabled && - (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && - !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { - /* Try to get an address on this netif that is invalid. - * Skip 0 index (link-local address) */ - for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { - if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) { - /* Generate an address using this prefix and interface ID from link-local address. */ - prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0]; - prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1]; - prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2]; - prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3]; - - /* Mark it as tentative (DAD will be performed if configured). */ - netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); - - /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ - prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; - - /* Exit loop. */ - break; - } - } - } -#endif /* LWIP_IPV6_AUTOCONFIG */ - } - } - - - /* Process our own addresses, if DAD configured. */ - for (netif = netif_list; netif != NULL; netif = netif->next) { - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { - if (ip6_addr_istentative(netif->ip6_addr_state[i])) { - if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { - /* No NA received in response. Mark address as valid. */ - netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; - /* TODO implement preferred and valid lifetimes. */ - } - else if (netif->flags & NETIF_FLAG_UP) { -#if LWIP_IPV6_MLD - if ((netif->ip6_addr_state[i] & 0x07) == 0) { - /* Join solicited node multicast group. */ - ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]); - mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address); - } -#endif /* LWIP_IPV6_MLD */ - /* Send a NS for this address. */ - nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); - (netif->ip6_addr_state[i])++; - /* TODO send max 1 NS per tmr call? enable return*/ - /*return;*/ - } - } - } - } - -#if LWIP_IPV6_SEND_ROUTER_SOLICIT - /* Send router solicitation messages, if necessary. */ - for (netif = netif_list; netif != NULL; netif = netif->next) { - if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) { - nd6_send_rs(netif); - netif->rs_count--; - } - } -#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ - -} - -/** - * Send a neighbor solicitation message - * - * @param netif the netif on which to send the message - * @param target_addr the IPv6 target address for the ND message - * @param flags one of ND6_SEND_FLAG_* - */ -static void -nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) -{ - struct ns_header * ns_hdr; - struct lladdr_option * lladdr_opt; - struct pbuf * p; - ip6_addr_t * src_addr; - - if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { - /* Use link-local address as source address. */ - src_addr = netif_ip6_addr(netif, 0); - } else { - src_addr = IP6_ADDR_ANY; - } - - /* Allocate a packet. */ - p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM); - if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) { - /* We couldn't allocate a suitable pbuf for the ns. drop it. */ - if (p != NULL) { - pbuf_free(p); - } - ND6_STATS_INC(nd6.memerr); - return; - } - - /* Set fields. */ - ns_hdr = (struct ns_header *)p->payload; - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); - - ns_hdr->type = ICMP6_TYPE_NS; - ns_hdr->code = 0; - ns_hdr->chksum = 0; - ns_hdr->reserved = 0; - ip6_addr_set(&(ns_hdr->target_address), target_addr); - - lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; - lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); - SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); - - /* Generate the solicited node address for the target address. */ - if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { - ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); - target_addr = &multicast_address; - } - - ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, - target_addr); - - /* Send the packet out. */ - ND6_STATS_INC(nd6.xmit); - ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr, - LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); - pbuf_free(p); -} - -/** - * Send a neighbor advertisement message - * - * @param netif the netif on which to send the message - * @param target_addr the IPv6 target address for the ND message - * @param flags one of ND6_SEND_FLAG_* - */ -static void -nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) -{ - struct na_header * na_hdr; - struct lladdr_option * lladdr_opt; - struct pbuf * p; - ip6_addr_t * src_addr; - ip6_addr_t * dest_addr; - - /* Use link-local address as source address. */ - /* src_addr = &(netif->ip6_addr[0]); */ - /* Use target address as source address. */ - src_addr = target_addr; - - /* Allocate a packet. */ - p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM); - if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) { - /* We couldn't allocate a suitable pbuf for the ns. drop it. */ - if (p != NULL) { - pbuf_free(p); - } - ND6_STATS_INC(nd6.memerr); - return; - } - - /* Set fields. */ - na_hdr = (struct na_header *)p->payload; - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); - - na_hdr->type = ICMP6_TYPE_NA; - na_hdr->code = 0; - na_hdr->chksum = 0; - na_hdr->flags = flags & 0xf0; - na_hdr->reserved[0] = 0; - na_hdr->reserved[1] = 0; - na_hdr->reserved[2] = 0; - ip6_addr_set(&(na_hdr->target_address), target_addr); - - lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; - lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); - SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); - - /* Generate the solicited node address for the target address. */ - if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { - ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); - dest_addr = &multicast_address; - } - else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { - ip6_addr_set_allnodes_linklocal(&multicast_address); - dest_addr = &multicast_address; - } - else { - dest_addr = ip6_current_src_addr(); - } - - na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, - dest_addr); - - /* Send the packet out. */ - ND6_STATS_INC(nd6.xmit); - ip6_output_if(p, src_addr, dest_addr, - LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); - pbuf_free(p); -} - -#if LWIP_IPV6_SEND_ROUTER_SOLICIT -/** - * Send a router solicitation message - * - * @param netif the netif on which to send the message - */ -static void -nd6_send_rs(struct netif * netif) -{ - struct rs_header * rs_hdr; - struct lladdr_option * lladdr_opt; - struct pbuf * p; - ip6_addr_t * src_addr; - u16_t packet_len; - - /* Link-local source address, or unspecified address? */ - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { - src_addr = netif_ip6_addr(netif, 0); - } - else { - src_addr = IP6_ADDR_ANY; - } - - /* Generate the all routers target address. */ - ip6_addr_set_allrouters_linklocal(&multicast_address); - - /* Allocate a packet. */ - packet_len = sizeof(struct rs_header); - if (src_addr != IP6_ADDR_ANY) { - packet_len += sizeof(struct lladdr_option); - } - p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM); - if ((p == NULL) || (p->len < packet_len)) { - /* We couldn't allocate a suitable pbuf for the ns. drop it. */ - if (p != NULL) { - pbuf_free(p); - } - ND6_STATS_INC(nd6.memerr); - return; - } - - /* Set fields. */ - rs_hdr = (struct rs_header *)p->payload; - - rs_hdr->type = ICMP6_TYPE_RS; - rs_hdr->code = 0; - rs_hdr->chksum = 0; - rs_hdr->reserved = 0; - - if (src_addr != IP6_ADDR_ANY) { - /* Include our hw address. */ - lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); - lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; - lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); - SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); - } - - rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, - &multicast_address); - - /* Send the packet out. */ - ND6_STATS_INC(nd6.xmit); - ip6_output_if(p, src_addr, &multicast_address, - LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); - pbuf_free(p); -} -#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ - -/** - * Search for a neighbor cache entry - * - * @param ip6addr the IPv6 address of the neighbor - * @return The neighbor cache entry index that matched, -1 if no - * entry is found - */ -static s8_t -nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr) -{ - s8_t i; - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { - return i; - } - } - return -1; -} - -/** - * Create a new neighbor cache entry. - * - * If no unused entry is found, will try to recycle an old entry - * according to ad-hoc "age" heuristic. - * - * @return The neighbor cache entry index that was created, -1 if no - * entry could be created - */ -static s8_t -nd6_new_neighbor_cache_entry(void) -{ - s8_t i; - s8_t j; - u32_t time; - - - /* First, try to find an empty entry. */ - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if (neighbor_cache[i].state == ND6_NO_ENTRY) { - return i; - } - } - - /* We need to recycle an entry. in general, do not recycle if it is a router. */ - - /* Next, try to find a Stale entry. */ - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if ((neighbor_cache[i].state == ND6_STALE) && - (!neighbor_cache[i].isrouter)) { - nd6_free_neighbor_cache_entry(i); - return i; - } - } - - /* Next, try to find a Probe entry. */ - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if ((neighbor_cache[i].state == ND6_PROBE) && - (!neighbor_cache[i].isrouter)) { - nd6_free_neighbor_cache_entry(i); - return i; - } - } - - /* Next, try to find a Delayed entry. */ - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if ((neighbor_cache[i].state == ND6_DELAY) && - (!neighbor_cache[i].isrouter)) { - nd6_free_neighbor_cache_entry(i); - return i; - } - } - - /* Next, try to find the oldest reachable entry. */ - time = 0xfffffffful; - j = -1; - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if ((neighbor_cache[i].state == ND6_REACHABLE) && - (!neighbor_cache[i].isrouter)) { - if (neighbor_cache[i].counter.reachable_time < time) { - j = i; - time = neighbor_cache[i].counter.reachable_time; - } - } - } - if (j >= 0) { - nd6_free_neighbor_cache_entry(j); - return j; - } - - /* Next, find oldest incomplete entry without queued packets. */ - time = 0; - j = -1; - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if ( - (neighbor_cache[i].q == NULL) && - (neighbor_cache[i].state == ND6_INCOMPLETE) && - (!neighbor_cache[i].isrouter)) { - if (neighbor_cache[i].counter.probes_sent >= time) { - j = i; - time = neighbor_cache[i].counter.probes_sent; - } - } - } - if (j >= 0) { - nd6_free_neighbor_cache_entry(j); - return j; - } - - /* Next, find oldest incomplete entry with queued packets. */ - time = 0; - j = -1; - for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { - if ((neighbor_cache[i].state == ND6_INCOMPLETE) && - (!neighbor_cache[i].isrouter)) { - if (neighbor_cache[i].counter.probes_sent >= time) { - j = i; - time = neighbor_cache[i].counter.probes_sent; - } - } - } - if (j >= 0) { - nd6_free_neighbor_cache_entry(j); - return j; - } - - /* No more entries to try. */ - return -1; -} - -/** - * Will free any resources associated with a neighbor cache - * entry, and will mark it as unused. - * - * @param i the neighbor cache entry index to free - */ -static void -nd6_free_neighbor_cache_entry(s8_t i) -{ - if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { - return; - } - - /* Free any queued packets. */ - if (neighbor_cache[i].q != NULL) { - nd6_free_q(neighbor_cache[i].q); - neighbor_cache[i].q = NULL; - } - - neighbor_cache[i].state = ND6_NO_ENTRY; - neighbor_cache[i].isrouter = 0; - neighbor_cache[i].netif = NULL; - neighbor_cache[i].counter.reachable_time = 0; - ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); -} - -/** - * Search for a destination cache entry - * - * @param ip6addr the IPv6 address of the destination - * @return The destination cache entry index that matched, -1 if no - * entry is found - */ -static s8_t -nd6_find_destination_cache_entry(ip6_addr_t * ip6addr) -{ - s8_t i; - for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { - if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { - return i; - } - } - return -1; -} - -/** - * Create a new destination cache entry. If no unused entry is found, - * will recycle oldest entry. - * - * @return The destination cache entry index that was created, -1 if no - * entry was created - */ -static s8_t -nd6_new_destination_cache_entry(void) -{ - s8_t i, j; - u32_t age; - - /* Find an empty entry. */ - for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { - if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { - return i; - } - } - - /* Find oldest entry. */ - age = 0; - j = LWIP_ND6_NUM_DESTINATIONS - 1; - for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { - if (destination_cache[i].age > age) { - j = i; - } - } - - return j; -} - -/** - * Determine whether an address matches an on-link prefix. - * - * @param ip6addr the IPv6 address to match - * @return 1 if the address is on-link, 0 otherwise - */ -static s8_t -nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif) -{ - s8_t i; - for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { - if ((prefix_list[i].netif == netif) && - (prefix_list[i].invalidation_timer > 0) && - ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { - return 1; - } - } - /* Check to see if address prefix matches a (manually?) configured address. */ - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { - return 1; - } - } - return 0; -} - -/** - * Select a default router for a destination. - * - * @param ip6addr the destination address - * @param netif the netif for the outgoing packet, if known - * @return the default router entry index, or -1 if no suitable - * router is found - */ -s8_t -nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif) -{ - s8_t i; - /* last_router is used for round-robin router selection (as recommended - * in RFC). This is more robust in case one router is not reachable, - * we are not stuck trying to resolve it. */ - static s8_t last_router; - (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ - - /* TODO: implement default router preference */ - - /* Look for reachable routers. */ - for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { - if (++last_router >= LWIP_ND6_NUM_ROUTERS) { - last_router = 0; - } - if ((default_router_list[i].neighbor_entry != NULL) && - (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && - (default_router_list[i].invalidation_timer > 0) && - (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { - return i; - } - } - - /* Look for router in other reachability states, but still valid according to timer. */ - for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { - if (++last_router >= LWIP_ND6_NUM_ROUTERS) { - last_router = 0; - } - if ((default_router_list[i].neighbor_entry != NULL) && - (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && - (default_router_list[i].invalidation_timer > 0)) { - return i; - } - } - - /* Look for any router for which we have any information at all. */ - for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { - if (++last_router >= LWIP_ND6_NUM_ROUTERS) { - last_router = 0; - } - if (default_router_list[i].neighbor_entry != NULL && - (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { - return i; - } - } - - /* no suitable router found. */ - return -1; -} - -/** - * Find an entry for a default router. - * - * @param router_addr the IPv6 address of the router - * @param netif the netif on which the router is found, if known - * @return the index of the router entry, or -1 if not found - */ -static s8_t -nd6_get_router(ip6_addr_t * router_addr, struct netif * netif) -{ - s8_t i; - - /* Look for router. */ - for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { - if ((default_router_list[i].neighbor_entry != NULL) && - ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && - ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { - return i; - } - } - - /* router not found. */ - return -1; -} - -/** - * Create a new entry for a default router. - * - * @param router_addr the IPv6 address of the router - * @param netif the netif on which the router is connected, if known - * @return the index on the router table, or -1 if could not be created - */ -static s8_t -nd6_new_router(ip6_addr_t * router_addr, struct netif * netif) -{ - s8_t router_index; - s8_t neighbor_index; - - /* Do we have a neighbor entry for this router? */ - neighbor_index = nd6_find_neighbor_cache_entry(router_addr); - if (neighbor_index < 0) { - /* Create a neighbor entry for this router. */ - neighbor_index = nd6_new_neighbor_cache_entry(); - if (neighbor_index < 0) { - /* Could not create neighbor entry for this router. */ - return -1; - } - ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); - neighbor_cache[neighbor_index].netif = netif; - neighbor_cache[neighbor_index].q = NULL; - neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; - neighbor_cache[neighbor_index].counter.probes_sent = 0; - } - - /* Mark neighbor as router. */ - neighbor_cache[neighbor_index].isrouter = 1; - - /* Look for empty entry. */ - for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { - if (default_router_list[router_index].neighbor_entry == NULL) { - default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); - return router_index; - } - } - - /* Could not create a router entry. */ - - /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ - neighbor_cache[neighbor_index].isrouter = 0; - - /* router not found. */ - return -1; -} - -/** - * Find the cached entry for an on-link prefix. - * - * @param prefix the IPv6 prefix that is on-link - * @param netif the netif on which the prefix is on-link - * @return the index on the prefix table, or -1 if not found - */ -static s8_t -nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) -{ - s8_t i; - - /* Look for prefix in list. */ - for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { - if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && - (prefix_list[i].netif == netif)) { - return i; - } - } - - /* Entry not available. */ - return -1; -} - -/** - * Creates a new entry for an on-link prefix. - * - * @param prefix the IPv6 prefix that is on-link - * @param netif the netif on which the prefix is on-link - * @return the index on the prefix table, or -1 if not created - */ -static s8_t -nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) -{ - s8_t i; - - /* Create new entry. */ - for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { - if ((prefix_list[i].netif == NULL) || - (prefix_list[i].invalidation_timer == 0)) { - /* Found empty prefix entry. */ - prefix_list[i].netif = netif; - ip6_addr_set(&(prefix_list[i].prefix), prefix); - prefix_list[i].flags = 0; - return i; - } - } - - /* Entry not available. */ - return -1; -} - -/** - * Determine the next hop for a destination. Will determine if the - * destination is on-link, else a suitable on-link router is selected. - * - * The last entry index is cached for fast entry search. - * - * @param ip6addr the destination address - * @param netif the netif on which the packet will be sent - * @return the neighbor cache entry for the next hop, ERR_RTE if no - * suitable next hop was found, ERR_MEM if no cache entry - * could be created - */ -s8_t -nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif) -{ - s8_t i; - -#if LWIP_NETIF_HWADDRHINT - if (netif->addr_hint != NULL) { - /* per-pcb cached entry was given */ - u8_t addr_hint = *(netif->addr_hint); - if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { - nd6_cached_destination_index = addr_hint; - } - } -#endif /* LWIP_NETIF_HWADDRHINT */ - - /* Look for ip6addr in destination cache. */ - if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { - /* the cached entry index is the right one! */ - /* do nothing. */ - ND6_STATS_INC(nd6.cachehit); - } else { - /* Search destination cache. */ - i = nd6_find_destination_cache_entry(ip6addr); - if (i >= 0) { - /* found destination entry. make it our new cached index. */ - nd6_cached_destination_index = i; - } - else { - /* Not found. Create a new destination entry. */ - i = nd6_new_destination_cache_entry(); - if (i >= 0) { - /* got new destination entry. make it our new cached index. */ - nd6_cached_destination_index = i; - } else { - /* Could not create a destination cache entry. */ - return ERR_MEM; - } - - /* Copy dest address to destination cache. */ - ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); - - /* Now find the next hop. is it a neighbor? */ - if (ip6_addr_islinklocal(ip6addr) || - nd6_is_prefix_in_netif(ip6addr, netif)) { - /* Destination in local link. */ - destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; - ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); - } - else { - /* We need to select a router. */ - i = nd6_select_router(ip6addr, netif); - if (i < 0) { - /* No router found. */ - ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); - return ERR_RTE; - } - destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ - ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); - } - } - } - -#if LWIP_NETIF_HWADDRHINT - if (netif->addr_hint != NULL) { - /* per-pcb cached entry was given */ - *(netif->addr_hint) = nd6_cached_destination_index; - } -#endif /* LWIP_NETIF_HWADDRHINT */ - - /* Look in neighbor cache for the next-hop address. */ - if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), - &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { - /* Cache hit. */ - /* Do nothing. */ - ND6_STATS_INC(nd6.cachehit); - } else { - i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); - if (i >= 0) { - /* Found a matching record, make it new cached entry. */ - nd6_cached_neighbor_index = i; - } - else { - /* Neighbor not in cache. Make a new entry. */ - i = nd6_new_neighbor_cache_entry(); - if (i >= 0) { - /* got new neighbor entry. make it our new cached index. */ - nd6_cached_neighbor_index = i; - } else { - /* Could not create a neighbor cache entry. */ - return ERR_MEM; - } - - /* Initialize fields. */ - ip6_addr_copy(neighbor_cache[i].next_hop_address, - destination_cache[nd6_cached_destination_index].next_hop_addr); - neighbor_cache[i].isrouter = 0; - neighbor_cache[i].netif = netif; - neighbor_cache[i].state = ND6_INCOMPLETE; - neighbor_cache[i].counter.probes_sent = 0; - } - } - - /* Reset this destination's age. */ - destination_cache[nd6_cached_destination_index].age = 0; - - return nd6_cached_neighbor_index; -} - -/** - * Queue a packet for a neighbor. - * - * @param neighbor_index the index in the neighbor cache table - * @param q packet to be queued - * @return ERR_OK if succeeded, ERR_MEM if out of memory - */ -err_t -nd6_queue_packet(s8_t neighbor_index, struct pbuf * q) -{ - err_t result = ERR_MEM; - struct pbuf *p; - int copy_needed = 0; -#if LWIP_ND6_QUEUEING - struct nd6_q_entry *new_entry, *r; -#endif /* LWIP_ND6_QUEUEING */ - - if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { - return ERR_ARG; - } - - /* 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) { - 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_LINK, q->tot_len, PBUF_RAM); - while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { - /* Free oldest packet (as per RFC recommendation) */ -#if LWIP_ND6_QUEUEING - r = neighbor_cache[neighbor_index].q; - neighbor_cache[neighbor_index].q = r->next; - r->next = NULL; - nd6_free_q(r); -#else /* LWIP_ND6_QUEUEING */ - pbuf_free(neighbor_cache[neighbor_index].q); - neighbor_cache[neighbor_index].q = NULL; -#endif /* LWIP_ND6_QUEUEING */ - p = pbuf_alloc(PBUF_LINK, q->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 was copied/ref'd? */ - if (p != NULL) { - /* queue packet ... */ -#if LWIP_ND6_QUEUEING - /* allocate a new nd6 queue entry */ - new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); - if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { - /* Free oldest packet (as per RFC recommendation) */ - r = neighbor_cache[neighbor_index].q; - neighbor_cache[neighbor_index].q = r->next; - r->next = NULL; - nd6_free_q(r); - new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); - } - if (new_entry != NULL) { - new_entry->next = NULL; - new_entry->p = p; - if(neighbor_cache[neighbor_index].q != NULL) { - /* queue was already existent, append the new entry to the end */ - r = neighbor_cache[neighbor_index].q; - while (r->next != NULL) { - r = r->next; - } - r->next = new_entry; - } else { - /* queue did not exist, first item in queue */ - neighbor_cache[neighbor_index].q = new_entry; - } - LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); - result = ERR_OK; - } else { - /* the pool MEMP_ND6_QUEUE is empty */ - pbuf_free(p); - LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); - /* { result == ERR_MEM } through initialization */ - } -#else /* LWIP_ND6_QUEUEING */ - /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ - if (neighbor_cache[neighbor_index].q != NULL) { - pbuf_free(neighbor_cache[neighbor_index].q); - } - neighbor_cache[neighbor_index].q = p; - LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); - result = ERR_OK; -#endif /* LWIP_ND6_QUEUEING */ - } else { - LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); - /* { result == ERR_MEM } through initialization */ - } - - return result; -} - -#if LWIP_ND6_QUEUEING -/** - * Free a complete queue of nd6 q entries - * - * @param q a queue of nd6_q_entry to free - */ -static void -nd6_free_q(struct nd6_q_entry *q) -{ - struct nd6_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_ND6_QUEUE, r); - } -} -#endif /* LWIP_ND6_QUEUEING */ - -/** - * Send queued packets for a neighbor - * - * @param i the neighbor to send packets to - */ -static void -nd6_send_q(s8_t i) -{ - struct ip6_hdr *ip6hdr; -#if LWIP_ND6_QUEUEING - struct nd6_q_entry *q; -#endif /* LWIP_ND6_QUEUEING */ - - if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { - return; - } - -#if LWIP_ND6_QUEUEING - while (neighbor_cache[i].q != NULL) { - /* remember first in queue */ - q = neighbor_cache[i].q; - /* pop first item off the queue */ - neighbor_cache[i].q = q->next; - /* Get ipv6 header. */ - ip6hdr = (struct ip6_hdr *)(q->p->payload); - /* Override ip6_current_dest_addr() so that we have an aligned copy. */ - ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); - /* send the queued IPv6 packet */ - (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr()); - /* free the queued IP packet */ - pbuf_free(q->p); - /* now queue entry can be freed */ - memp_free(MEMP_ND6_QUEUE, q); - } -#else /* LWIP_ND6_QUEUEING */ - if (neighbor_cache[i].q != NULL) { - /* Get ipv6 header. */ - ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); - /* Override ip6_current_dest_addr() so that we have an aligned copy. */ - ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); - /* send the queued IPv6 packet */ - (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr()); - /* free the queued IP packet */ - pbuf_free(neighbor_cache[i].q); - neighbor_cache[i].q = NULL; - } -#endif /* LWIP_ND6_QUEUEING */ -} - - -/** - * Get the Path MTU for a destination. - * - * @param ip6addr the destination address - * @param netif the netif on which the packet will be sent - * @return the Path MTU, if known, or the netif default MTU - */ -u16_t -nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif) -{ - s8_t i; - - i = nd6_find_destination_cache_entry(ip6addr); - if (i >= 0) { - if (destination_cache[i].pmtu > 0) { - return destination_cache[i].pmtu; - } - } - - if (netif != NULL) { - return netif->mtu; - } - - return 1280; /* Minimum MTU */ -} - - -#if LWIP_ND6_TCP_REACHABILITY_HINTS -/** - * Provide the Neighbor discovery process with a hint that a - * destination is reachable. Called by tcp_receive when ACKs are - * received or sent (as per RFC). This is useful to avoid sending - * NS messages every 30 seconds. - * - * @param ip6addr the destination address which is know to be reachable - * by an upper layer protocol (TCP) - */ -void -nd6_reachability_hint(ip6_addr_t * ip6addr) -{ - s8_t i; - - /* Find destination in cache. */ - if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { - i = nd6_cached_destination_index; - ND6_STATS_INC(nd6.cachehit); - } - else { - i = nd6_find_destination_cache_entry(ip6addr); - } - if (i < 0) { - return; - } - - /* Find next hop neighbor in cache. */ - if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { - i = nd6_cached_neighbor_index; - ND6_STATS_INC(nd6.cachehit); - } - else { - i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); - } - if (i < 0) { - return; - } - - /* Set reachability state. */ - neighbor_cache[i].state = ND6_REACHABLE; - neighbor_cache[i].counter.reachable_time = reachable_time; -} -#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ - -#endif /* LWIP_IPV6 */ diff --git a/third_party/lwip/core/mem.c b/third_party/lwip/core/mem.c deleted file mode 100644 index 3375255..0000000 --- a/third_party/lwip/core/mem.c +++ /dev/null @@ -1,660 +0,0 @@ -/** - * @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 - * 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 - -#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 * ICACHE_FLASH_ATTR -mem_malloc(mem_size_t size) -{ - void *ret; - struct memp_malloc_helper *element; - memp_t poolnr; - mem_size_t required_size = size + LWIP_MEM_ALIGN_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 */ - ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); - - return ret; -} - -/** - * 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 ICACHE_FLASH_ATTR -mem_free(void *rmem) -{ - struct memp_malloc_helper *hmem; - - 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 = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); - - 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 */ -#if !NO_SYS -static sys_mutex_t mem_mutex; -#endif - -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 or mem_trim */ - 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 or mem_trim to run */ - LWIP_MEM_ALLOC_PROTECT(); - if (mem_free_count != 0) { - /* If mem_free or mem_trim have run, we have to restart since they - could have altered our current struct mem. */ - local_mem_free_count = 1; - break; - } -#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 LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT -mem_malloc_adjust_lfree: -#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ - if (mem == lfree) { - struct mem *cur = lfree; - /* Find next free block after mem and update lowest free pointer */ - while (cur->used && cur != ram_end) { -#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT - mem_free_count = 0; - LWIP_MEM_ALLOC_UNPROTECT(); - /* prevent high interrupt latency... */ - LWIP_MEM_ALLOC_PROTECT(); - if (mem_free_count != 0) { - /* If mem_free or mem_trim have run, we have to restart since they - could have altered our current struct mem or lfree. */ - goto mem_malloc_adjust_lfree; - } -#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ - cur = (struct mem *)(void *)&ram[cur->next]; - } - lfree = cur; - 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 * ICACHE_FLASH_ATTR -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/third_party/lwip/core/memp.c b/third_party/lwip/core/memp.c deleted file mode 100644 index 8dc0086..0000000 --- a/third_party/lwip/core/memp.c +++ /dev/null @@ -1,485 +0,0 @@ -/** - * @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 - * - */ - -#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 "lwip/nd6.h" -#include "lwip/ip6_frag.h" -#include "lwip/mld6.h" - -#include - -#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, using "Floyd's cycle-finding algorithm". - */ -static int ICACHE_FLASH_ATTR -memp_sanity(void) -{ - s16_t i; - struct memp *t, *h; - - for (i = 0; i < MEMP_MAX; i++) { - t = memp_tab[i]; - if(t != NULL) { - for (h = t->next; (t != NULL) && (h != NULL); t = t->next, - h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { - if (t == h) { - 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -memp_overflow_check_all(void) -{ - u16_t i, j; - struct memp *p; - -#if !MEMP_SEPARATE_POOLS - p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); -#endif /* !MEMP_SEPARATE_POOLS */ - for (i = 0; i < MEMP_MAX; ++i) { -#if MEMP_SEPARATE_POOLS - p = (struct memp *)(memp_bases[i]); -#endif /* MEMP_SEPARATE_POOLS */ - 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); - } - } -#if !MEMP_SEPARATE_POOLS - p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); -#endif /* !MEMP_SEPARATE_POOLS */ - for (i = 0; i < MEMP_MAX; ++i) { -#if MEMP_SEPARATE_POOLS - p = (struct memp *)(memp_bases[i]); -#endif /* MEMP_SEPARATE_POOLS */ - 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 ICACHE_FLASH_ATTR -memp_overflow_init(void) -{ - u16_t i, j; - struct memp *p; - u8_t *m; - -#if !MEMP_SEPARATE_POOLS - p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); -#endif /* !MEMP_SEPARATE_POOLS */ - for (i = 0; i < MEMP_MAX; ++i) { -#if MEMP_SEPARATE_POOLS - p = (struct memp *)(memp_bases[i]); -#endif /* MEMP_SEPARATE_POOLS */ - 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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -#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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/netif.c b/third_party/lwip/core/netif.c deleted file mode 100644 index 239ff90..0000000 --- a/third_party/lwip/core/netif.c +++ /dev/null @@ -1,902 +0,0 @@ -/** - * @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 - * - */ - -#include "lwip/opt.h" - -#include "lwip/def.h" -#include "lwip/ip_addr.h" -#include "lwip/ip6_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_IPV6_DHCP6 -#include "lwip/dhcp6.h" -#endif /* LWIP_IPV6_DHCP6 */ -#if LWIP_IPV6_MLD -#include "lwip/mld6.h" -#endif /* LWIP_IPV6_MLD */ - -#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; - -static u8_t netif_num; - -#if LWIP_IPV6 -static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); -#endif /* LWIP_IPV6 */ - -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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) -{ -#if LWIP_IPV6 - u32_t i; -#endif - - 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); -#if LWIP_IPV6 - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - ip6_addr_set_zero(&netif->ip6_addr[i]); - netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); - } - netif->output_ip6 = netif_null_output_ip6; -#endif /* LWIP_IPV6 */ - 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_IPV6_AUTOCONFIG - /* IPv6 address autoconfiguration not enabled by default */ - netif->ip6_autoconfig_enabled = 0; -#endif /* LWIP_IPV6_AUTOCONFIG */ -#if LWIP_IPV6_SEND_ROUTER_SOLICIT - netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; -#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ -#if LWIP_IPV6_DHCP6 - /* netif not under DHCPv6 control by default */ - netif->dhcp6 = NULL; -#endif /* LWIP_IPV6_DHCP6 */ -#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 LWIP_IPV6 && LWIP_IPV6_MLD - netif->mld_mac_filter = NULL; -#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ -#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 = netif_num++; - netif->input = input; - NETIF_SET_HWADDRHINT(netif, NULL); -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 LWIP_IPV6 && LWIP_IPV6_MLD - /* stop MLD processing */ - mld6_stop(netif); -#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ - 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); - } -#if LWIP_NETIF_REMOVE_CALLBACK - if (netif->remove_callback) { - netif->remove_callback(netif); - } -#endif /* LWIP_NETIF_REMOVE_CALLBACK */ - 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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 (ipaddr && (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(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr)) -#if LWIP_AUTOIP - /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ - && !ip_addr_islinklocal(ipX_2_ip(&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(ipX_2_ip(&lpcb->local_ip)))) && - (ip_addr_cmp(ipX_2_ip(&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(ipX_2_ip(&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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ -#if LWIP_IPV6 && LWIP_IPV6_MLD - /* send mld memberships */ - mld6_report_groups( netif); -#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ - -#if LWIP_IPV6_SEND_ROUTER_SOLICIT - /* Send Router Solicitation messages. */ - netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; -#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ - - } - } -} - -/** - * 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 ICACHE_FLASH_ATTR -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 - -#if LWIP_ARP - if (netif->flags & NETIF_FLAG_ETHARP) { - etharp_cleanup_netif(netif); - } -#endif /* LWIP_ARP */ - NETIF_STATUS_CALLBACK(netif); - } -} - -#if LWIP_NETIF_STATUS_CALLBACK -/** - * Set callback to be called when interface is brought up/down - */ -void ICACHE_FLASH_ATTR -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 */ - -#if LWIP_NETIF_REMOVE_CALLBACK -/** - * Set callback to be called when the interface has been removed - */ -void ICACHE_FLASH_ATTR -netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) -{ - if (netif) { - netif->remove_callback = remove_callback; - } -} -#endif /* LWIP_NETIF_REMOVE_CALLBACK */ - -/** - * Called by a driver when its link goes up - */ -void ICACHE_FLASH_ATTR -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 */ -#if LWIP_IPV6 && LWIP_IPV6_MLD - /* send mld memberships */ - mld6_report_groups( netif); -#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ - } - NETIF_LINK_CALLBACK(netif); - } -} - -/** - * Called by a driver when its link goes down - */ -void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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_with_block((tcpip_callback_fn)netif_poll, netif, 0); -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ - -#if LWIP_IPV6 -s8_t ICACHE_FLASH_ATTR -netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr) -{ - s8_t i; - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { - if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { - return i; - } - } - return -1; -} - -void ICACHE_FLASH_ATTR -netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit) -{ - u8_t i, addr_index; - - /* Link-local prefix. */ - netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul); - netif->ip6_addr[0].addr[1] = 0; - - /* Generate interface ID. */ - if (from_mac_48bit) { - /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ - netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | - ((u32_t)(netif->hwaddr[1]) << 16) | - ((u32_t)(netif->hwaddr[2]) << 8) | - (0xff)); - netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) | - ((u32_t)(netif->hwaddr[3]) << 16) | - ((u32_t)(netif->hwaddr[4]) << 8) | - (netif->hwaddr[5])); - } - else { - /* Use hwaddr directly as interface ID. */ - netif->ip6_addr[0].addr[2] = 0; - netif->ip6_addr[0].addr[3] = 0; - - addr_index = 3; - for (i = 0; i < 8; i++) { - if (i == 4) { - addr_index--; - } - netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); - } - } - - /* Set address state. */ -#if LWIP_IPV6_DUP_DETECT_ATTEMPTS - /* Will perform duplicate address detection (DAD). */ - netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; -#else - /* Consider address valid. */ - netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; -#endif /* LWIP_IPV6_AUTOCONFIG */ -} - -static err_t ICACHE_FLASH_ATTR -netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) -{ - (void)netif; - (void)p; - (void)ipaddr; - - return ERR_IF; -} -#endif /* LWIP_IPV6 */ diff --git a/third_party/lwip/core/pbuf.c b/third_party/lwip/core/pbuf.c deleted file mode 100644 index 3d4df2a..0000000 --- a/third_party/lwip/core/pbuf.c +++ /dev/null @@ -1,1235 +0,0 @@ -/** - * @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 - * - */ - -#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 LWIP_TCP && TCP_QUEUE_OOSEQ -#include "lwip/tcp_impl.h" -#endif -#if LWIP_CHECKSUM_ON_COPY -#include "lwip/inet_chksum.h" -#endif - -#include - -#ifdef EBUF_LWIP -#include "pp/esf_buf.h" -#else -#define EP_OFFSET 0 -#endif /* ESF_LWIP */ - -#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 || !PBUF_POOL_FREE_OOSEQ -#define PBUF_POOL_IS_EMPTY() -#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ - -#if !NO_SYS -#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL -#include "lwip/tcpip.h" -#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ - if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ - SYS_ARCH_PROTECT(old_level); \ - pbuf_free_ooseq_pending = 0; \ - SYS_ARCH_UNPROTECT(old_level); \ - } } while(0) -#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ -#endif /* !NO_SYS */ - -volatile u8_t pbuf_free_ooseq_pending; -#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() - -/** - * 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. - */ -#if !NO_SYS -static -#endif /* !NO_SYS */ -void ICACHE_FLASH_ATTR -pbuf_free_ooseq(void) -{ - struct tcp_pcb* pcb; - SYS_ARCH_DECL_PROTECT(old_level); - - SYS_ARCH_PROTECT(old_level); - pbuf_free_ooseq_pending = 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; - } - } -} - -#if !NO_SYS -/** - * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). - */ -static void ICACHE_FLASH_ATTR -pbuf_free_ooseq_callback(void *arg) -{ - LWIP_UNUSED_ARG(arg); - pbuf_free_ooseq(); -} -#endif /* !NO_SYS */ - -/** Queue a call to pbuf_free_ooseq if not already queued. */ -static void ICACHE_FLASH_ATTR -pbuf_pool_is_empty(void) -{ -#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL - SYS_ARCH_DECL_PROTECT(old_level); - SYS_ARCH_PROTECT(old_level); - pbuf_free_ooseq_pending = 1; - SYS_ARCH_UNPROTECT(old_level); -#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ - u8_t queued; - SYS_ARCH_DECL_PROTECT(old_level); - SYS_ARCH_PROTECT(old_level); - queued = pbuf_free_ooseq_pending; - pbuf_free_ooseq_pending = 1; - SYS_ARCH_UNPROTECT(old_level); - - if(!queued) { - /* queue a call to pbuf_free_ooseq if not already queued */ - PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); - } -#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ -} -#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ - -/** - * 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 * ICACHE_FLASH_ATTR -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 */ - switch (layer) { - case PBUF_TRANSPORT: - /* add room for transport (often TCP) layer header */ - offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN + EP_OFFSET; - break; - case PBUF_IP: - /* add room for IP layer header */ - offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + EP_OFFSET; - break; - case PBUF_LINK: - /* add room for link layer header */ - offset = PBUF_LINK_HLEN; - -#ifdef PBUF_RSV_FOR_WLAN - /* - * 1. LINK_HLEN 14Byte will be remove in WLAN layer - * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. - * 3. encryption needs exra 4 bytes ahead of actual data payload, and require - * DAddr and SAddr to be 4-byte aligned. - * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... - * 5. LCC add 6 bytes more, We don't consider WAPI yet... - * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be - * matter is ether_hdr is not 4B aligned. - * - * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned - * - * 1. lwip - * | empty 30B | eth_hdr (14B) | payload ...| - * total: 44B ahead payload - * 2. net80211 - * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| - * total: 40B ahead sec_rsv and 44B ahead payload - * - */ - offset += EP_OFFSET; //remove LINK hdr in wlan -#endif /* PBUF_RSV_FOR_WLAN */ - - break; - case PBUF_RAW: -#ifdef PBUF_RSV_FOR_WLAN - /* - * RAW pbuf suppose - */ - offset = EP_OFFSET; //remove LINK hdr in wlan -#endif /* 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; -#ifdef EBUF_LWIP - case PBUF_ESF_RX: -#endif /* ESF_LWIP */ - /* 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. - * ATTENTION: The caller is responsible for correct alignment of this buffer!! - * @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* ICACHE_FLASH_ATTR -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 */ - switch (l) { - case PBUF_TRANSPORT: - /* add room for transport (often TCP) layer header */ - offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; - break; - case PBUF_IP: - /* add room for IP layer header */ - offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; - break; - case PBUF_LINK: - /* add room for link layer header */ - offset = PBUF_LINK_HLEN; - break; - case PBUF_RAW: - offset = 0; - 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 = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ - if (type == PBUF_REF) { - /* increase payload pointer */ - p->payload = (u8_t *)p->payload - header_size_increment; - } else { - 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 ICACHE_FLASH_ATTR -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 -#ifdef EBUF_LWIP - || p->type == PBUF_ESF_RX -#endif //EBUF_LWIP - ); - - 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 -#ifdef EBUF_LWIP - || type == PBUF_ESF_RX -#endif //EBUF_LWIP - ) { -#ifdef EBUF_LWIP - esf_buf *eb = p->eb; - ppRecycleRxPkt(eb); -#endif //EBUF_LWIP - 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 - { - /* 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); - 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 (offset_to == p_to->len) { - /* on to next p_to (if any) */ - offset_to = 0; - p_to = p_to->next; - LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); - } - - 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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* ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/raw.c b/third_party/lwip/core/raw.c deleted file mode 100644 index 8672ae2..0000000 --- a/third_party/lwip/core/raw.c +++ /dev/null @@ -1,422 +0,0 @@ -/** - * @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 - * - */ - -#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 "lwip/ip6.h" -#include "lwip/ip6_addr.h" - -#include - -/** 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 ICACHE_FLASH_ATTR -raw_input(struct pbuf *p, struct netif *inp) -{ - struct raw_pcb *pcb, *prev; - struct ip_hdr *iphdr; - s16_t proto; - u8_t eaten = 0; -#if LWIP_IPV6 - struct ip6_hdr *ip6hdr; -#endif /* LWIP_IPV6 */ - - - LWIP_UNUSED_ARG(inp); - - iphdr = (struct ip_hdr *)p->payload; -#if LWIP_IPV6 - if (IPH_V(iphdr) == 6) { - ip6hdr = (struct ip6_hdr *)p->payload; - proto = IP6H_NEXTH(ip6hdr); - } - else -#endif /* LWIP_IPV6 */ - { - 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_PCB_IPVER_INPUT_MATCH(pcb) && - (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) || - ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) { -#if IP_SOF_BROADCAST_RECV - /* broadcast filter? */ - if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) -#if LWIP_IPV6 - && !PCB_ISIPV6(pcb) -#endif /* LWIP_IPV6 */ - ) -#endif /* IP_SOF_BROADCAST_RECV */ - { - /* receive callback function available? */ - if (pcb->recv.ip4 != NULL) { -#ifndef LWIP_NOASSERT - void* old_payload = p->payload; -#endif - /* the receive callback function did not eat the packet? */ - eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr()); - if (eaten != 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; - } - } else { - /* sanity-check that the receive callback did not alter the pbuf */ - LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", - p->payload == old_payload); - } - } - /* 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 ICACHE_FLASH_ATTR -raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) -{ - ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &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 ICACHE_FLASH_ATTR -raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) -{ - ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &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 ICACHE_FLASH_ATTR -raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) -{ - /* remember recv() callback and user data */ - pcb->recv.ip4 = 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 ICACHE_FLASH_ATTR -raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) -{ - err_t err; - struct netif *netif; - ipX_addr_t *src_ip; - struct pbuf *q; /* q will be sent down the stack */ - s16_t header_size; - ipX_addr_t *dst_ip = ip_2_ipX(ipaddr); - - LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); - - header_size = ( -#if LWIP_IPV6 - PCB_ISIPV6(pcb) ? IP6_HLEN : -#endif /* LWIP_IPV6 */ - IP_HLEN); - - /* not enough space to add an IP header to first pbuf in given p chain? */ - if (pbuf_header(p, header_size)) { - /* 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, -header_size)) { - LWIP_ASSERT("Can't restore header we just removed!", 0); - return ERR_MEM; - } - } - - netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); - if (netif == NULL) { - LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); - /* free any temporary header pbuf allocated by pbuf_header() */ - if (q != p) { - pbuf_free(q); - } - return ERR_RTE; - } - -#if IP_SOF_BROADCAST -#if LWIP_IPV6 - /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */ - if (!PCB_ISIPV6(pcb)) -#endif /* LWIP_IPV6 */ - { - /* broadcast filter? */ - if (!ip_get_option(pcb, SOF_BROADCAST) && 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 (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { - /* use outgoing network interface IP address as source address */ - src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip); -#if LWIP_IPV6 - if (src_ip == NULL) { - if (q != p) { - pbuf_free(q); - } - return ERR_RTE; - } -#endif /* LWIP_IPV6 */ - } else { - /* use RAW PCB local IP address as source address */ - src_ip = &pcb->local_ip; - } - - NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); - err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); - NETIF_SET_HWADDRHINT(netif, NULL); - - /* 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 ICACHE_FLASH_ATTR -raw_send(struct raw_pcb *pcb, struct pbuf *p) -{ - return raw_sendto(pcb, p, ipX_2_ip(&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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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; -} - -#if LWIP_IPV6 -/** - * Create a RAW PCB for IPv6. - * - * @return The RAW PCB which was created. NULL if the PCB data structure - * could not be allocated. - * - * @param proto the protocol number (next header) of the IPv6 packet payload - * (e.g. IP6_NEXTH_ICMP6) - * - * @see raw_remove() - */ -struct raw_pcb * ICACHE_FLASH_ATTR -raw_new_ip6(u8_t proto) -{ - struct raw_pcb *pcb; - pcb = raw_new(proto); - ip_set_v6(pcb, 1); - return pcb; -} -#endif /* LWIP_IPV6 */ - -#endif /* LWIP_RAW */ diff --git a/third_party/lwip/core/snmp/asn1_dec.c b/third_party/lwip/core/snmp/asn1_dec.c deleted file mode 100644 index ffa536e..0000000 --- a/third_party/lwip/core/snmp/asn1_dec.c +++ /dev/null @@ -1,657 +0,0 @@ -/** - * @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 - */ - -#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/third_party/lwip/core/snmp/asn1_enc.c b/third_party/lwip/core/snmp/asn1_enc.c deleted file mode 100644 index a6169dc..0000000 --- a/third_party/lwip/core/snmp/asn1_enc.c +++ /dev/null @@ -1,611 +0,0 @@ -/** - * @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 - */ - -#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/third_party/lwip/core/snmp/mib2.c b/third_party/lwip/core/snmp/mib2.c deleted file mode 100644 index 1fa9e68..0000000 --- a/third_party/lwip/core/snmp/mib2.c +++ /dev/null @@ -1,4146 +0,0 @@ -/** - * @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 - */ - -#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) (sysuptime = (sys_now() / 10)) -#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(ipX_2_ip(&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(ipX_2_ip(&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 (ipX_addr_cmp(0, &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, ðaddr_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, ðaddr_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; - ipX_addr_t ip; - u16_t port; - - LWIP_UNUSED_ARG(len); - snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&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) && - !(ipX_addr_cmp(0, &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 */ - { - ipX_addr_t *dst = (ipX_addr_t*)value; - ipX_addr_copy(0, *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/third_party/lwip/core/snmp/mib_structs.c b/third_party/lwip/core/snmp/mib_structs.c deleted file mode 100644 index cffbc70..0000000 --- a/third_party/lwip/core/snmp/mib_structs.c +++ /dev/null @@ -1,1174 +0,0 @@ -/** - * @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 - */ - -#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/third_party/lwip/core/snmp/msg_in.c b/third_party/lwip/core/snmp/msg_in.c deleted file mode 100644 index 2dfbdc9..0000000 --- a/third_party/lwip/core/snmp/msg_in.c +++ /dev/null @@ -1,1453 +0,0 @@ -/** - * @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 - */ - -#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 - -/* 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; istate = 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, this 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) -{ - /* move names back from outvb to invb */ - int v; - struct snmp_varbind *vbi = msg_ps->invb.head; - struct snmp_varbind *vbo = msg_ps->outvb.head; - for (v=0; vvb_idx; v++) { - vbi->ident_len = vbo->ident_len; - vbo->ident_len = 0; - vbi->ident = vbo->ident; - vbo->ident = NULL; - vbi = vbi->next; - vbo = vbo->next; - } - /* free outvb */ - snmp_varbind_list_free(&msg_ps->outvb); - /* we send invb back */ - 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; - /* error index must be 0 for error too big */ - msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0; - 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); - 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); - 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); - 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); - 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; - vb->ident = NULL; - vb->ident_len = 0; - 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); - 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); - if (vb->ident == NULL) - { - LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n")); - 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); - if (vb->value == NULL) - { - LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n")); - 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; - } - } - else - { - LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n")); - } - 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/third_party/lwip/core/snmp/msg_out.c b/third_party/lwip/core/snmp/msg_out.c deleted file mode 100644 index b1dd565..0000000 --- a/third_party/lwip/core/snmp/msg_out.c +++ /dev/null @@ -1,678 +0,0 @@ -/** - * @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 - */ - -#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); - 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; - err_t err = ERR_OK; - - for (i=0, td = &trap_dst[0]; ienable != 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); - if (dst_if != NULL) { - 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 { - err = ERR_MEM; - } - } else { - /* routing error */ - err = ERR_RTE; - } - } - } - return err; -} - -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/third_party/lwip/core/stats.c b/third_party/lwip/core/stats.c deleted file mode 100644 index cc232f7..0000000 --- a/third_party/lwip/core/stats.c +++ /dev/null @@ -1,182 +0,0 @@ -/** - * @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 - * - */ - -#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 - -struct stats_ lwip_stats; - -void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -stats_display_proto(struct stats_proto *proto, const 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 ICACHE_FLASH_ATTR -stats_display_igmp(struct stats_igmp *igmp, const char *name) -{ - LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); - 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\t", igmp->rx_group)); - LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", 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 ICACHE_FLASH_ATTR -stats_display_mem(struct stats_mem *mem, const 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -stats_display(void) -{ - s16_t i; - - LINK_STATS_DISPLAY(); - ETHARP_STATS_DISPLAY(); - IPFRAG_STATS_DISPLAY(); - IP6_FRAG_STATS_DISPLAY(); - IP_STATS_DISPLAY(); - ND6_STATS_DISPLAY(); - IP6_STATS_DISPLAY(); - IGMP_STATS_DISPLAY(); - MLD6_STATS_DISPLAY(); - ICMP_STATS_DISPLAY(); - ICMP6_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/third_party/lwip/core/sys.c b/third_party/lwip/core/sys.c deleted file mode 100644 index e3aa6fc..0000000 --- a/third_party/lwip/core/sys.c +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @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 - * - */ - -#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 - -#ifndef sys_msleep -/** - * Sleep for some ms. Timeouts are NOT processed while sleeping. - * - * @param ms number of milliseconds to sleep - */ -void ICACHE_FLASH_ATTR -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 /* sys_msleep */ - -#endif /* !NO_SYS */ diff --git a/third_party/lwip/core/tcp.c b/third_party/lwip/core/tcp.c deleted file mode 100644 index 9ffba07..0000000 --- a/third_party/lwip/core/tcp.c +++ /dev/null @@ -1,1820 +0,0 @@ -/** - * @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 - * - */ - -#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 "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/nd6.h" - -#include - -#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 -#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) -#endif - -#if LWIP_TCP_KEEPALIVE -#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) -#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) -#else /* LWIP_TCP_KEEPALIVE */ -#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE -#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT -#endif /* LWIP_TCP_KEEPALIVE */ - -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" -}; - -/* last local TCP port */ -static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; - -/* 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; - -u8_t tcp_active_pcbs_changed; - -/** Timer counter to handle calling slow-timer from tcp_tmr() */ -static u8_t tcp_timer; -static u8_t tcp_timer_ctr; -static u16_t tcp_new_port(void); - -/** - * Initialize this module. - */ -void ICACHE_FLASH_ATTR -tcp_init(void) -{ -#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) - tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); -#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ -} - -/** - * Called periodically to dispatch TCP timers. - */ -void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) -{ - err_t err; - - if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { - 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, PCB_ISIPV6(pcb)); - - tcp_pcb_purge(pcb); - TCP_RMV_ACTIVE(pcb); - if (pcb->state == ESTABLISHED) { - /* move to TIME_WAIT since we close actively */ - pcb->state = TIME_WAIT; - TCP_REG(&tcp_tw_pcbs, pcb); - } else { - /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ - memp_free(MEMP_TCP_PCB, 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_ACTIVE(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 ICACHE_FLASH_ATTR -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 unless shutting down both sides! - * Shutting down both sides is the same as calling tcp_close, so if it succeds, - * the PCB should not be referenced any more. - * - * @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 ICACHE_FLASH_ATTR -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: set a flag not to receive any more data... */ - pcb->flags |= TF_RXCLOSED; - if (shut_tx) { - /* shutting down the tx AND rx side is the same as closing for the raw API */ - return tcp_close_shutdown(pcb, 1); - } - /* ... and free buffered data */ - if (pcb->refused_data != NULL) { - pbuf_free(pcb->refused_data); - pcb->refused_data = NULL; - } - } - 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, shut_rx); - default: - /* Not (yet?) connected, cannot shutdown the TX side as that would bring us - into CLOSED state, where the PCB is deallocated. */ - return ERR_CONN; - } - } - 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 ICACHE_FLASH_ATTR -tcp_abandon(struct tcp_pcb *pcb, int reset) -{ - u32_t seqno, ackno; -#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 { - int send_rst = reset && (pcb->state != CLOSED); - seqno = pcb->snd_nxt; - ackno = pcb->rcv_nxt; -#if LWIP_CALLBACK_API - errf = pcb->errf; -#endif /* LWIP_CALLBACK_API */ - errf_arg = pcb->callback_arg; - TCP_PCB_REMOVE_ACTIVE(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 */ - if (send_rst) { - LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); - tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); - } - memp_free(MEMP_TCP_PCB, pcb); - TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); - } -} - -/** - * 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 (ip_get_option(pcb, SOF_REUSEADDR)) { - max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; - } -#endif /* SO_REUSE */ - - if (port == 0) { - port = tcp_new_port(); - if (port == 0) { - return ERR_BUF; - } - } - - /* 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 (!ip_get_option(pcb, SOF_REUSEADDR) || - !ip_get_option(cpcb, SOF_REUSEADDR)) -#endif /* SO_REUSE */ - { - /* @todo: check accept_any_ip_version */ - if (IP_PCB_IPVER_EQ(pcb, cpcb) && - (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) || - ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) || - ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) { - return ERR_USE; - } - } - } - } - } - - if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) { - ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 (ip_get_option(pcb, SOF_REUSEADDR)) { - /* 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) && - IP_PCB_IPVER_EQ(pcb, lpcb)) { - if (ipX_addr_cmp(PCB_ISIPV6(pcb), &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; - ip_set_option(lpcb, SOF_ACCEPTCONN); - lpcb->ttl = pcb->ttl; - lpcb->tos = pcb->tos; -#if LWIP_IPV6 - PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb); - lpcb->accept_any_ip_version = 0; -#endif /* LWIP_IPV6 */ - ipX_addr_copy(PCB_ISIPV6(pcb), 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; -} - -#if LWIP_IPV6 -/** - * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6 - * connections, if the pcb's local address is set to ANY. - */ -struct tcp_pcb * ICACHE_FLASH_ATTR -tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog) -{ - struct tcp_pcb *lpcb; - - lpcb = tcp_listen_with_backlog(pcb, backlog); - if ((lpcb != NULL) && - ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { - /* The default behavior is to accept connections on either - * IPv4 or IPv6, if not bound. */ - /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */ - ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1; - } - return lpcb; -} -#endif /* LWIP_IPV6 */ - -/** - * 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -tcp_recved(struct tcp_pcb *pcb, u16_t len) -{ - int wnd_inflation; - - /* pcb->state LISTEN not allowed here */ - LWIP_ASSERT("don't call tcp_recved for listen-pcbs", - pcb->state != LISTEN); - 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: received %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", - len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); -} - -/** - * Allocate a new local TCP port. - * - * @return a new (free) local TCP port number - */ -static u16_t ICACHE_FLASH_ATTR -tcp_new_port(void) -{ - u8_t i; - u16_t n = 0; - struct tcp_pcb *pcb; - -again: - if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { - tcp_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 == tcp_port) { - if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { - return 0; - } - goto again; - } - } - } - return tcp_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 ICACHE_FLASH_ATTR -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) { - ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr)); - } else { - return ERR_VAL; - } - pcb->remote_port = port; - - /* check if we have a route to the remote host */ - if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { - /* no local IP address set, yet. */ - struct netif *netif; - ipX_addr_t *local_ip; - ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); - if ((netif == NULL) || (local_ip == NULL)) { - /* Don't even try to send a SYN packet if we have no route - since that will fail. */ - return ERR_RTE; - } - /* Use the address as local address of the pcb. */ - ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); - } - - old_local_port = pcb->local_port; - if (pcb->local_port == 0) { - pcb->local_port = tcp_new_port(); - if (pcb->local_port == 0) { - return ERR_BUF; - } - } -#if SO_REUSE - if (ip_get_option(pcb, SOF_REUSEADDR)) { - /* 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_PCB_IPVER_EQ(cpcb, pcb) && - ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) && - ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(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, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); -#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_ACTIVE(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 ICACHE_FLASH_ATTR -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; - ++tcp_timer_ctr; - -tcp_slowtmr_start: - /* 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); - if (pcb->last_timer == tcp_timer_ctr) { - /* skip this pcb, we have already processed it */ - pcb = pcb->next; - continue; - } - pcb->last_timer = tcp_timer_ctr; - - 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 this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ - if (pcb->flags & TF_RXCLOSED) { - /* PCB was fully closed (either through close() or SHUT_RDWR): - normal FIN-WAIT timeout handling. */ - 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(ip_get_option(pcb, SOF_KEEPALIVE) && - ((pcb->state == ESTABLISHED) || - (pcb->state == CLOSE_WAIT))) { - if((u32_t)(tcp_ticks - pcb->tmr) > - (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) - { - LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); - LWIP_DEBUGF(TCP_DEBUG, ("\n")); - - ++pcb_remove; - ++pcb_reset; - } - else if((u32_t)(tcp_ticks - pcb->tmr) > - (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) - / TCP_SLOW_INTERVAL) - { - 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_err_fn err_fn; - void *err_arg; - 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; - } - - if (pcb_reset) { - tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, - pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); - } - - err_fn = pcb->errf; - err_arg = pcb->callback_arg; - pcb2 = pcb; - pcb = pcb->next; - memp_free(MEMP_TCP_PCB, pcb2); - - tcp_active_pcbs_changed = 0; - TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); - if (tcp_active_pcbs_changed) { - goto tcp_slowtmr_start; - } - } 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_active_pcbs_changed = 0; - TCP_EVENT_POLL(prev, err); - if (tcp_active_pcbs_changed) { - goto tcp_slowtmr_start; - } - /* 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 ICACHE_FLASH_ATTR -tcp_fasttmr(void) -{ - struct tcp_pcb *pcb; - - ++tcp_timer_ctr; - -tcp_fasttmr_start: - pcb = tcp_active_pcbs; - - while(pcb != NULL) { - if (pcb->last_timer != tcp_timer_ctr) { - struct tcp_pcb *next; - pcb->last_timer = tcp_timer_ctr; - /* send delayed ACKs */ - if (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); - } - - next = pcb->next; - - /* If there is data which was previously "refused" by upper layer */ - if (pcb->refused_data != NULL) { - tcp_active_pcbs_changed = 0; - tcp_process_refused_data(pcb); - if (tcp_active_pcbs_changed) { - /* application callback has changed the pcb list: restart the loop */ - goto tcp_fasttmr_start; - } - } - pcb = next; - } - } -} - -/** Pass pcb->refused_data to the recv callback */ -err_t ICACHE_FLASH_ATTR -tcp_process_refused_data(struct tcp_pcb *pcb) -{ - err_t err; - u8_t refused_flags = pcb->refused_data->flags; - /* set pcb->refused_data to NULL in case the callback frees it and then - closes the pcb */ - struct pbuf *refused_data = pcb->refused_data; - 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, refused_data, ERR_OK, err); - if (err == ERR_OK) { - /* did refused_data include a FIN? */ - if (refused_flags & PBUF_FLAG_TCP_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) { - return ERR_ABRT; - } - } - } else if (err == ERR_ABRT) { - /* 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")); - return ERR_ABRT; - } else { - /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ - pcb->refused_data = refused_data; - } - return ERR_OK; -} - -/** - * Deallocates a list of TCP segments (tcp_seg structures). - * - * @param seg tcp_seg list of TCP segments to free - */ -void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 the same or lower priority than - * 'prio'. - * - * @param prio minimum priority - */ -static void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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->last_timer = tcp_timer_ctr; - - 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 * ICACHE_FLASH_ATTR -tcp_new(void) -{ - return tcp_alloc(TCP_PRIO_NORMAL); -} - -#if LWIP_IPV6 -/** - * Creates a new TCP-over-IPv6 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(). - * - * @return a new tcp_pcb that initially is in state CLOSED - */ -struct tcp_pcb * ICACHE_FLASH_ATTR -tcp_new_ip6(void) -{ - struct tcp_pcb * pcb; - pcb = tcp_alloc(TCP_PRIO_NORMAL); - ip_set_v6(pcb, 1); - return pcb; -} -#endif /* LWIP_IPV6 */ - -/** - * 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 ICACHE_FLASH_ATTR -tcp_arg(struct tcp_pcb *pcb, void *arg) -{ - /* This function is allowed to be called for both listen pcbs and - connection pcbs. */ - 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 ICACHE_FLASH_ATTR -tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) -{ - LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); - 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 ICACHE_FLASH_ATTR -tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) -{ - LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); - 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 ICACHE_FLASH_ATTR -tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) -{ - LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); - 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 ICACHE_FLASH_ATTR -tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) -{ - /* This function is allowed to be called for both listen pcbs and - connection pcbs. */ - 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 ICACHE_FLASH_ATTR -tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) -{ - LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); -#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 ICACHE_FLASH_ATTR -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_PCB_IPVER_EQ(pcb, lpcb) && - (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) || - ipX_addr_cmp(PCB_ISIPV6(lpcb), &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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest -#if LWIP_IPV6 - , ipX_addr_t *src, u8_t isipv6 -#endif /* LWIP_IPV6 */ - ) -{ - u16_t mss_s; - struct netif *outif; - s16_t mtu; - - outif = ipX_route(isipv6, src, dest); -#if LWIP_IPV6 - if (isipv6) { - /* First look in destination cache, to see if there is a Path MTU. */ - mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif); - } else -#endif /* LWIP_IPV6 */ - { - if (outif == NULL) { - return sendmss; - } - mtu = outif->mtu; - } - - if (mtu != 0) { - mss_s = mtu - IP_HLEN - TCP_HLEN; -#if LWIP_IPV6 - /* for IPv6, substract the difference in header size */ - mss_s -= (IP6_HLEN - IP_HLEN); -#endif /* LWIP_IPV6 */ - /* 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* ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/tcp_in.c b/third_party/lwip/core/tcp_in.c deleted file mode 100644 index a255810..0000000 --- a/third_party/lwip/core/tcp_in.c +++ /dev/null @@ -1,1652 +0,0 @@ -/** - * @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 - * - */ - -#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" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/inet_chksum.h" -#if LWIP_ND6_TCP_REACHABILITY_HINTS -#include "lwip/nd6.h" -#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ - -/* 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 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 TCP header) - * @param inp network interface on which this segment was received - */ -void ICACHE_FLASH_ATTR -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; -#if CHECKSUM_CHECK_TCP - u16_t chksum; -#endif /* CHECKSUM_CHECK_TCP */ - - PERF_START; - - TCP_STATS_INC(tcp.recv); - snmp_inc_tcpinsegs(); - - tcphdr = (struct tcp_hdr *)p->payload; - -#if TCP_INPUT_DEBUG - tcp_debug_print(tcphdr); -#endif - - /* Check that TCP header fits in payload */ - if (p->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); - goto dropped; - } - - /* Don't even process incoming broadcasts/multicasts. */ - if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) || - ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) { - TCP_STATS_INC(tcp.proterr); - goto dropped; - } - -#if CHECKSUM_CHECK_TCP - /* Verify TCP checksum. */ - chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len, - ipX_current_src_addr(), ipX_current_dest_addr()); - if (chksum != 0) { - LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", - chksum)); - tcp_debug_print(tcphdr); - TCP_STATS_INC(tcp.chkerr); - goto dropped; - } -#endif /* CHECKSUM_CHECK_TCP */ - - /* 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); - goto dropped; - } - - /* 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_PCB_IPVER_INPUT_MATCH(pcb) && - ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && - ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { - /* 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_PCB_IPVER_INPUT_MATCH(pcb) && - ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && - ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { - /* 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 LWIP_IPV6 - if (lpcb->accept_any_ip_version) { - /* found an ANY-match */ -#if SO_REUSE - lpcb_any = lpcb; - lpcb_prev = prev; -#else /* SO_REUSE */ - break; -#endif /* SO_REUSE */ - } else -#endif /* LWIP_IPV6 */ - if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) { - if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) { - /* found an exact match */ - break; - } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) { - /* found an ANY-match */ -#if SO_REUSE - lpcb_any = lpcb; - lpcb_prev = prev; -#else /* SO_REUSE */ - 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 (flags & TCP_PSH) { - p->flags |= PBUF_FLAG_PUSH; - } - - /* If there is data which was previously "refused" by upper layer */ - if (pcb->refused_data != NULL) { - if ((tcp_process_refused_data(pcb) == ERR_ABRT) || - ((pcb->refused_data != NULL) && (tcplen > 0))) { - /* pcb has been aborted or refused data is still refused and the new - segment contains data */ - TCP_STATS_INC(tcp.drop); - snmp_inc_tcpinerrs(); - goto aborted; - } - } - 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. */ - if (!(pcb->flags & TF_RXCLOSED)) { - /* Connection closed although the application has only shut down the - tx side: call the PCB's err callback and indicate the closure to - ensure the application doesn't continue using the PCB. */ - TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); - } - 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; - } - - /* 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) { - if (pcb->refused_data != NULL) { - /* Delay this if we have refused data. */ - pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; - } else { - /* 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, ipX_current_dest_addr(), - ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); - } - pbuf_free(p); - } - - LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); - PERF_STOP("tcp_input"); - return; -dropped: - TCP_STATS_INC(tcp.drop); - snmp_inc_tcpinerrs(); - pbuf_free(p); -} - -/** - * 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 ICACHE_FLASH_ATTR -tcp_listen_input(struct tcp_pcb_listen *pcb) -{ - struct tcp_pcb *npcb; - err_t rc; - - if (flags & TCP_RST) { - /* An incoming RST should be ignored. Return. */ - return ERR_OK; - } - - /* 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, seqno + tcplen, ipX_current_dest_addr(), - ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); - } 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. */ -#if LWIP_IPV6 - PCB_ISIPV6(npcb) = ip_current_is_v6(); -#endif /* LWIP_IPV6 */ - ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr()); - ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr()); - npcb->local_port = pcb->local_port; - 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->snd_wnd_max = 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_ACTIVE(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->local_ip, - &npcb->remote_ip, PCB_ISIPV6(npcb)); -#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 ICACHE_FLASH_ATTR -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, ipX_current_dest_addr(), - ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); - 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 ICACHE_FLASH_ATTR -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_wnd_max = 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->local_ip, &pcb->remote_ip, - PCB_ISIPV6(pcb)); -#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; - tcp_seg_free(rseg); - - /* 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; - } - - /* 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, ipX_current_dest_addr(), - ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); - } - 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, ipX_current_dest_addr(), - ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); - } - } 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_ACTIVE(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_ACTIVE(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_ACTIVE(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 ICACHE_FLASH_ATTR -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 - * 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 ICACHE_FLASH_ATTR -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 TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS - u32_t ooseq_blen; - u16_t ooseq_qlen; -#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ - - LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); - - 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; - /* keep track of the biggest window announced by the remote host to calculate - the maximum segment size */ - if (pcb->snd_wnd_max < tcphdr->wnd) { - pcb->snd_wnd_max = tcphdr->wnd; - } - pcb->snd_wl1 = seqno; - pcb->snd_wl2 = ackno; - if (pcb->snd_wnd == 0) { - if (pcb->persist_backoff == 0) { - /* start persist timer */ - pcb->persist_cnt = 0; - pcb->persist_backoff = 1; - } - } else if (pcb->persist_backoff > 0) { - /* stop persist timer */ - 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 ((u8_t)(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; - -#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS - if (PCB_ISIPV6(pcb)) { - /* Inform neighbor reachability of forward progress. */ - nd6_reachability_hint(ip6_current_src_addr()); - } -#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ - } 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; -#if TCP_OVERSIZE - if (pcb->unsent == NULL) { - pcb->unsent_oversize = 0; - } -#endif /* TCP_OVERSIZE */ - 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 unless the pcb already received a FIN. - (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, - LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ - if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { - /* 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); - -#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS - if (PCB_ISIPV6(pcb)) { - /* Inform neighbor reachability of forward progress. */ - nd6_reachability_hint(ip6_current_src_addr()); - } -#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ - - } 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; - } - } -#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS - /* Check that the data on ooseq doesn't exceed one of the limits - and throw away everything above that limit. */ - ooseq_blen = 0; - ooseq_qlen = 0; - prev = NULL; - for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { - struct pbuf *p = next->p; - ooseq_blen += p->tot_len; - ooseq_qlen += pbuf_clen(p); - if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || - (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { - /* too much ooseq data, dump this and everything after it */ - tcp_segs_free(next); - if (prev == NULL) { - /* first ooseq segment is too much, dump the whole queue */ - pcb->ooseq = NULL; - } else { - /* just dump 'next' and everything after it */ - prev->next = NULL; - } - break; - } - } -#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ -#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 ICACHE_FLASH_ATTR -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/third_party/lwip/core/tcp_out.c b/third_party/lwip/core/tcp_out.c deleted file mode 100644 index 4e8c9e1..0000000 --- a/third_party/lwip/core/tcp_out.c +++ /dev/null @@ -1,1499 +0,0 @@ -/** - * @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 - * - */ - -#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/ip_addr.h" -#include "lwip/netif.h" -#include "lwip/inet_chksum.h" -#include "lwip/stats.h" -#include "lwip/snmp.h" -#include "lwip/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/inet_chksum.h" -#if LWIP_TCP_TIMESTAMPS -#include "lwip/sys.h" -#endif - -#include - -/* 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 * ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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); - pcb->flags |= TF_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 * ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 = max_length; -#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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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; - /* don't allocate segments bigger than half the maximum window we ever received */ - u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); - -#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 = mss_local - (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 = mss_local - 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, mss_local, &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 - LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", - last_unsent->oversize_left >= oversize_used); - 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -tcp_send_empty_ack(struct tcp_pcb *pcb) -{ - struct pbuf *p; - u8_t optlen = 0; -#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP - struct tcp_hdr *tcphdr; -#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ - -#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; - } -#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP - tcphdr = (struct tcp_hdr *)p->payload; -#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ - 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 = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, - &pcb->local_ip, &pcb->remote_ip); -#endif -#if LWIP_NETIF_HWADDRHINT - ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, - IP_PROTO_TCP, &pcb->addr_hint); -#else /* LWIP_NETIF_HWADDRHINT*/ - ipX_output(PCB_ISIPV6(pcb), 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 ICACHE_FLASH_ATTR -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 */ - - /* pcb->state LISTEN not allowed here */ - LWIP_ASSERT("don't call tcp_output for listen-pcbs", - pcb->state != LISTEN); - - /* 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); - } - -#if TCP_OVERSIZE_DBGCHECK - seg->oversize_left = 0; -#endif /* TCP_OVERSIZE_DBGCHECK */ - 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 */ - - 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 ICACHE_FLASH_ATTR -tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) -{ - u16_t len; - 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 */ - opts = (u32_t *)(void *)(seg->tcphdr + 1); - if (seg->flags & TF_SEG_OPTS_MSS) { - u16_t mss; -#if TCP_CALCULATE_EFF_SEND_MSS - mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); -#else /* TCP_CALCULATE_EFF_SEND_MSS */ - mss = TCP_MSS; -#endif /* TCP_CALCULATE_EFF_SEND_MSS */ - *opts = TCP_BUILD_MSS_OPTION(mss); - 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 (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { - struct netif *netif; - ipX_addr_t *local_ip; - ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); - if ((netif == NULL) || (local_ip == NULL)) { - return; - } - ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); - } - - 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 TCP_CHECKSUM_ON_COPY - { - u32_t acc; -#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK - u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, - seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); -#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 = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, - seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); - /* 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 */ -#if CHECKSUM_GEN_TCP - seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, - seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); -#endif /* CHECKSUM_GEN_TCP */ -#endif /* TCP_CHECKSUM_ON_COPY */ - TCP_STATS_INC(tcp.xmit); - -#if LWIP_NETIF_HWADDRHINT - ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, - pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); -#else /* LWIP_NETIF_HWADDRHINT*/ - ipX_output(PCB_ISIPV6(pcb), 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 ICACHE_FLASH_ATTR -tcp_rst_impl(u32_t seqno, u32_t ackno, - ipX_addr_t *local_ip, ipX_addr_t *remote_ip, - u16_t local_port, u16_t remote_port -#if LWIP_IPV6 - , u8_t isipv6 -#endif /* LWIP_IPV6 */ - ) -{ - 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; - - TCP_STATS_INC(tcp.xmit); - snmp_inc_tcpoutrsts(); - -#if CHECKSUM_GEN_TCP - tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len, - local_ip, remote_ip); -#endif - /* Send output with hardcoded TTL/HL since we have no access to the pcb */ - ipX_output(isipv6, 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 ICACHE_FLASH_ATTR -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; - /* last unsent hasn't changed, no need to reset unsent_oversize */ - - /* 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 ICACHE_FLASH_ATTR -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; -#if TCP_OVERSIZE - if (seg->next == NULL) { - /* the retransmitted segment is last in unsent, so reset unsent_oversize */ - pcb->unsent_oversize = 0; - } -#endif /* TCP_OVERSIZE */ - - ++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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -tcp_keepalive(struct tcp_pcb *pcb) -{ - struct pbuf *p; -#if CHECKSUM_GEN_TCP - struct tcp_hdr *tcphdr; -#endif /* CHECKSUM_GEN_TCP */ - - LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); - LWIP_DEBUGF(TCP_DEBUG, ("\n")); - - 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; - } -#if CHECKSUM_GEN_TCP - tcphdr = (struct tcp_hdr *)p->payload; - - tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, - &pcb->local_ip, &pcb->remote_ip); -#endif /* CHECKSUM_GEN_TCP */ - TCP_STATS_INC(tcp.xmit); - - /* Send output to IP */ -#if LWIP_NETIF_HWADDRHINT - ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, - pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); -#else /* LWIP_NETIF_HWADDRHINT*/ - ipX_output(PCB_ISIPV6(pcb), 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 ICACHE_FLASH_ATTR -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 ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); - LWIP_DEBUGF(TCP_DEBUG, ("\n")); - - 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 */ - char *d = ((char *)p->payload + TCP_HLEN); - /* Depending on whether the segment has already been sent (unacked) or not - (unsent), seg->p->payload points to the IP header or TCP header. - Ensure we copy the first TCP data byte: */ - pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); - } - -#if CHECKSUM_GEN_TCP - tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, - &pcb->local_ip, &pcb->remote_ip); -#endif - TCP_STATS_INC(tcp.xmit); - - /* Send output to IP */ -#if LWIP_NETIF_HWADDRHINT - ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, - 0, IP_PROTO_TCP, &pcb->addr_hint); -#else /* LWIP_NETIF_HWADDRHINT*/ - ipX_output(PCB_ISIPV6(pcb), 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/third_party/lwip/core/timers.c b/third_party/lwip/core/timers.c deleted file mode 100644 index 6f751cc..0000000 --- a/third_party/lwip/core/timers.c +++ /dev/null @@ -1,547 +0,0 @@ -/** - * @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 - * 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" -#include "lwip/nd6.h" -#include "lwip/ip6_frag.h" -#include "lwip/mld6.h" -#include "lwip/sys.h" -#include "lwip/pbuf.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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -arp_timer(void *arg) -{ - LWIP_UNUSED_ARG(arg); - LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); - etharp_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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 */ - -#if LWIP_IPV6 -/** - * Timer callback function that calls nd6_tmr() and reschedules itself. - * - * @param arg unused argument - */ -static void ICACHE_FLASH_ATTR -nd6_timer(void *arg) -{ - LWIP_UNUSED_ARG(arg); - LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); - nd6_tmr(); - sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); -} - -#if LWIP_IPV6_REASS -/** - * Timer callback function that calls ip6_reass_tmr() and reschedules itself. - * - * @param arg unused argument - */ -static void ICACHE_FLASH_ATTR -ip6_reass_timer(void *arg) -{ - LWIP_UNUSED_ARG(arg); - LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); - ip6_reass_tmr(); - sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); -} -#endif /* LWIP_IPV6_REASS */ - -#if LWIP_IPV6_MLD -/** - * Timer callback function that calls mld6_tmr() and reschedules itself. - * - * @param arg unused argument - */ -static void ICACHE_FLASH_ATTR -mld6_timer(void *arg) -{ - LWIP_UNUSED_ARG(arg); - LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); - mld6_tmr(); - sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); -} -#endif /* LWIP_IPV6_MLD */ -#endif /* LWIP_IPV6 */ - -/** Initialize this module */ -void ICACHE_FLASH_ATTR -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 LWIP_IPV6 - sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); -#if LWIP_IPV6_REASS - sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); -#endif /* LWIP_IPV6_REASS */ -#if LWIP_IPV6_MLD - sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); -#endif /* LWIP_IPV6_MLD */ -#endif /* LWIP_IPV6 */ - -#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 ICACHE_FLASH_ATTR -sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) -#else /* LWIP_DEBUG_TIMERNAMES */ -void ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -sys_check_timeouts(void) -{ - if (next_timeout) { - struct sys_timeo *tmptimeout; - u32_t diff; - sys_timeout_handler handler; - void *arg; - u8_t had_one; - u32_t now; - - now = sys_now(); - /* this cares for wraparounds */ - diff = now - timeouts_last_time; - do - { -#if PBUF_POOL_FREE_OOSEQ - PBUF_CHECK_FREE_OOSEQ(); -#endif /* PBUF_POOL_FREE_OOSEQ */ - had_one = 0; - tmptimeout = next_timeout; - if (tmptimeout && (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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -tcp_timer_needed(void) -{ -} -#endif /* LWIP_TIMERS */ diff --git a/third_party/lwip/core/udp.c b/third_party/lwip/core/udp.c deleted file mode 100644 index 4655c08..0000000 --- a/third_party/lwip/core/udp.c +++ /dev/null @@ -1,1151 +0,0 @@ -/** - * @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 - * - */ - - -/* 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/ip6.h" -#include "lwip/ip6_addr.h" -#include "lwip/inet_chksum.h" -#include "lwip/netif.h" -#include "lwip/icmp.h" -#include "lwip/icmp6.h" -#include "lwip/stats.h" -#include "lwip/snmp.h" -#include "arch/perf.h" -#include "lwip/dhcp.h" - -#include - -#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 -#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) -#endif - -/* last local UDP port */ -static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; - -/* The list of UDP PCBs */ -/* exported in udp.h (was static) */ -struct udp_pcb *udp_pcbs; - -/** - * Initialize this module. - */ -void ICACHE_FLASH_ATTR -udp_init(void) -{ -#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) - udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); -#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ -} - -/** - * Allocate a new local UDP port. - * - * @return a new (free) local UDP port number - */ -static u16_t ICACHE_FLASH_ATTR -udp_new_port(void) -{ - u16_t n = 0; - struct udp_pcb *pcb; - -again: - if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { - udp_port = UDP_LOCAL_PORT_RANGE_START; - } - /* Check all PCBs. */ - for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { - if (pcb->local_port == udp_port) { - if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { - return 0; - } - goto again; - } - } - return udp_port; -#if 0 - struct udp_pcb *ipcb = udp_pcbs; - while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) { - if (ipcb->local_port == udp_port) { - /* port is already used by another udp_pcb */ - udp_port++; - /* restart scanning all udp pcbs */ - ipcb = udp_pcbs; - } else { - /* go on with next udp pcb */ - ipcb = ipcb->next; - } - } - if (ipcb != NULL) { - return 0; - } - return udp_port; -#endif -} - -/** - * 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 (p->payload pointing to the UDP header) - * @param inp network interface on which the datagram was received. - * - */ -void ICACHE_FLASH_ATTR -udp_input(struct pbuf *p, struct netif *inp) -{ - struct udp_hdr *udphdr; - struct udp_pcb *pcb, *prev; - struct udp_pcb *uncon_pcb; - u16_t src, dest; - u8_t local_match; - u8_t broadcast; - u8_t for_us; - - PERF_START; - - UDP_STATS_INC(udp.recv); - - /* Check minimum length (UDP header) */ - if (p->len < UDP_HLEN) { - /* 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 ? */ -#if LWIP_IPV6 - broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp); -#else /* LWIP_IPV6 */ - broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp); -#endif /* LWIP_IPV6 */ - - 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 (")); - ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr()); - LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); - ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr()); - LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", 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 - (no need to check for IPv6 since the dhcp struct always uses IPv4) */ - if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) || - ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) { - 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 (")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip); - LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); - ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip); - LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); - - /* compare PCB local addr+port to UDP destination addr+port */ - if (pcb->local_port == dest) { - if ( -#if LWIP_IPV6 - ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) && - (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) || -#if LWIP_IPV6_MLD - ip6_addr_ismulticast(ip6_current_dest_addr()) || -#endif /* LWIP_IPV6_MLD */ - ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) || - (!PCB_ISIPV6(pcb) && - (ip_current_header() != NULL) && -#else /* LWIP_IPV6 */ - (( -#endif /* LWIP_IPV6 */ - ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) || - ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) || -#if LWIP_IGMP - ip_addr_ismulticast(ip_current_dest_addr()) || -#endif /* LWIP_IGMP */ -#if IP_SOF_BROADCAST_RECV - (broadcast && ip_get_option(pcb, SOF_BROADCAST) && - (ipX_addr_isany(0, &pcb->local_ip) || - ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { -#else /* IP_SOF_BROADCAST_RECV */ - (broadcast && - (ipX_addr_isany(0, &pcb->local_ip) || - ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { -#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_PCB_IPVER_INPUT_MATCH(pcb) && - (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) || - ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) { - /* 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) { - for_us = 1; - } else { -#if LWIP_IPV6 - if (ip_current_is_v6()) { - for_us = netif_matches_ip6_addr(inp, ip6_current_dest_addr()); - } else -#endif /* LWIP_IPV6 */ - { - for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()); - } - } - if (for_us) { - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); -#if CHECKSUM_CHECK_UDP -#if LWIP_UDPLITE - if (ip_current_header_proto() == IP_PROTO_UDPLITE) { - /* Do the UDP Lite checksum */ - 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) */ - goto chkerr; - } - } - if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE, - p->tot_len, chklen, - ipX_current_src_addr(), ipX_current_dest_addr()) != 0) { - goto chkerr; - } - } else -#endif /* LWIP_UDPLITE */ - { - if (udphdr->chksum != 0) { - if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len, - ipX_current_src_addr(), - ipX_current_dest_addr()) != 0) { - goto chkerr; - } - } - } -#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 || -#if LWIP_IPV6 - ip6_addr_ismulticast(ip6_current_dest_addr()) || -#endif /* LWIP_IPV6 */ - ip_addr_ismulticast(ip_current_dest_addr())) && - ip_get_option(pcb, SOF_REUSEADDR)) { - /* 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; - s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); - 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) && -#if LWIP_IPV6 - ((PCB_ISIPV6(mpcb) && - (ip6_addr_ismulticast(ip6_current_dest_addr()) || - ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) || - (!PCB_ISIPV6(mpcb) && -#else /* LWIP_IPV6 */ - (( -#endif /* LWIP_IPV6 */ - ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) || - ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) || -#if LWIP_IGMP - ip_addr_ismulticast(ip_current_dest_addr()) || -#endif /* LWIP_IGMP */ -#if IP_SOF_BROADCAST_RECV - (broadcast && ip_get_option(mpcb, 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.ip4 != NULL) { - struct pbuf *q; - /* for that, move payload to IP header again */ - if (p_header_changed == 0) { - pbuf_header(p, hdrs_len); - 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, -hdrs_len); -#if LWIP_IPV6 - if (PCB_ISIPV6(mpcb)) { - mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src); - } - else -#endif /* LWIP_IPV6 */ - { - mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); - } - } - } - } - } - } - } - if (p_header_changed) { - /* and move payload to UDP data again */ - pbuf_header(p, -hdrs_len); - } - } -#endif /* SO_REUSE && SO_REUSE_RXTOALL */ - /* callback */ - if (pcb->recv.ip4 != NULL) { - /* now the recv function is responsible for freeing p */ -#if LWIP_IPV6 - if (PCB_ISIPV6(pcb)) { - pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src); - } - else -#endif /* LWIP_IPV6 */ - { - pcb->recv.ip4(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 || LWIP_ICMP6 - /* No match was found, send ICMP destination port unreachable unless - destination address was broadcast/multicast. */ - if (!broadcast && -#if LWIP_IPV6 - !ip6_addr_ismulticast(ip6_current_dest_addr()) && -#endif /* LWIP_IPV6 */ - !ip_addr_ismulticast(ip_current_dest_addr())) { - /* move payload pointer back to ip header */ - pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); - icmp_port_unreach(ip_current_is_v6(), p); - } -#endif /* LWIP_ICMP || LWIP_ICMP6 */ - 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"); - return; -#if CHECKSUM_CHECK_UDP -chkerr: - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, - ("udp_input: UDP (or 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); - PERF_STOP("udp_input"); -#endif /* CHECKSUM_CHECK_UDP */ -} - -/** - * 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 ICACHE_FLASH_ATTR -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, ipX_2_ip(&pcb->remote_ip), pcb->remote_port); -} - -#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP -/** Same as udp_send() but with checksum - */ -err_t ICACHE_FLASH_ATTR -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, ipX_2_ip(&pcb->remote_ip), pcb->remote_port, - have_chksum, chksum); -} -#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ - -/** - * 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 ICACHE_FLASH_ATTR -udp_sendto(struct udp_pcb *pcb, struct pbuf *p, - ip_addr_t *dst_ip, u16_t dst_port) -{ -#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP - return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); -} - -/** Same as udp_sendto(), but with checksum */ -err_t ICACHE_FLASH_ATTR -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 && CHECKSUM_GEN_UDP */ - struct netif *netif; - ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip); - - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); - -#if LWIP_IPV6 || LWIP_IGMP - if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) { - /* For multicast, find a netif based on source address. */ -#if LWIP_IPV6 - if (PCB_ISIPV6(pcb)) { - dst_ip_route = &pcb->local_ip; - } else -#endif /* LWIP_IPV6 */ - { -#if LWIP_IGMP - dst_ip_route = ip_2_ipX(&pcb->multicast_ip); -#endif /* LWIP_IGMP */ - } - } -#endif /* LWIP_IPV6 || LWIP_IGMP */ - - /* find the outgoing network interface for this packet */ - netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route); - - /* no outgoing network interface could be found? */ - if (netif == NULL) { - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip)); - LWIP_DEBUGF(UDP_DEBUG, ("\n")); - UDP_STATS_INC(udp.rterr); - return ERR_RTE; - } -#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP - return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); -#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ - return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); -#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ -} - -/** - * 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 ICACHE_FLASH_ATTR -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 && CHECKSUM_GEN_UDP - return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); -} - -/** Same as udp_sendto_if(), but with checksum */ -err_t ICACHE_FLASH_ATTR -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 && CHECKSUM_GEN_UDP */ - struct udp_hdr *udphdr; - ip_addr_t *src_ip; - err_t err; - struct pbuf *q; /* q will be sent down the stack */ - u8_t ip_proto; - -#if IP_SOF_BROADCAST - /* broadcast filter? */ - if (!ip_get_option(pcb, SOF_BROADCAST) && -#if LWIP_IPV6 - !PCB_ISIPV6(pcb) && -#endif /* LWIP_IPV6 */ - 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, ipX_2_ip(&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 (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && -#if LWIP_IPV6 - ( -#if LWIP_IPV6_MLD - (PCB_ISIPV6(pcb) && - ip6_addr_ismulticast(ip_2_ip6(dst_ip))) || -#endif /* LWIP_IPV6_MLD */ - (!PCB_ISIPV6(pcb) && -#else /* LWIP_IPV6 */ - (( -#endif /* LWIP_IPV6 */ - ip_addr_ismulticast(dst_ip)))) { - q->flags |= PBUF_FLAG_MCASTLOOP; - } -#endif /* LWIP_IGMP */ - - - /* PCB local address is IP_ANY_ADDR? */ -#if LWIP_IPV6 - if (PCB_ISIPV6(pcb)) { - if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) { - src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip))); - if (src_ip == NULL) { - /* No suitable source address was found. */ - if (q != p) { - /* free the header pbuf */ - pbuf_free(q); - /* p is still referenced by the caller, and will live on */ - } - return ERR_RTE; - } - } else { - /* use UDP PCB local IPv6 address as source address, if still valid. */ - if (netif_matches_ip6_addr(netif, ipX_2_ip6(&pcb->local_ip)) < 0) { - /* Address isn't valid anymore. */ - if (q != p) { - /* free the header pbuf */ - pbuf_free(q); - /* p is still referenced by the caller, and will live on */ - } - return ERR_RTE; - } - src_ip = ipX_2_ip(&pcb->local_ip); - } - } - else -#endif /* LWIP_IPV6 */ - if (ip_addr_isany(ipX_2_ip(&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(ipX_2_ip(&(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 = ipX_2_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 -#if LWIP_CHECKSUM_ON_COPY - if (have_chksum) { - chklen = UDP_HLEN; - } -#endif /* LWIP_CHECKSUM_ON_COPY */ - udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE, - q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); -#if LWIP_CHECKSUM_ON_COPY - 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 */ - - ip_proto = IP_PROTO_UDPLITE; - } 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 - /* Checksum is mandatory over IPv6. */ - if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { - u16_t udpchksum; -#if LWIP_CHECKSUM_ON_COPY - if (have_chksum) { - u32_t acc; - udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, - q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); - acc = udpchksum + (u16_t)~(chksum); - udpchksum = FOLD_U32T(acc); - } else -#endif /* LWIP_CHECKSUM_ON_COPY */ - { - udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len, - ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); - } - - /* chksum zero must become 0xffff, as zero means 'no checksum' */ - if (udpchksum == 0x0000) { - udpchksum = 0xffff; - } - udphdr->chksum = udpchksum; - } -#endif /* CHECKSUM_GEN_UDP */ - ip_proto = IP_PROTO_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 (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); - /* output to IP */ - NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); - err = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif); - NETIF_SET_HWADDRHINT(netif, NULL); - - /* 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 ICACHE_FLASH_ATTR -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 = ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(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 (!ip_get_option(pcb, SOF_REUSEADDR) && - !ip_get_option(ipcb, SOF_REUSEADDR)) { -#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_PCB_IPVER_EQ(pcb, ipcb) && - /* IP address matches, or one is IP_ADDR_ANY? */ - (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) || - ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) || - ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(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; - } - } - } - - ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); - - /* no port specified? */ - if (port == 0) { - port = udp_new_port(); - if (port == 0) { - /* 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 ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", 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 ICACHE_FLASH_ATTR -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, ipX_2_ip(&pcb->local_ip), pcb->local_port); - if (err != ERR_OK) { - return err; - } - } - - ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); - pcb->remote_port = port; - pcb->flags |= UDP_FLAGS_CONNECTED; -/** TODO: this functionality belongs in upper layers */ -#ifdef LWIP_UDP_TODO -#if LWIP_IPV6 - if (!PCB_ISIPV6(pcb)) -#endif /* LWIP_IPV6 */ - { - /* Nail down local IP for netconn_addr()/getsockname() */ - if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { - struct netif *netif; - - if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) { - LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", - ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip)))); - 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! */ - ipX_addr_copy(0, pcb->local_ip, netif->ip_addr); - } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { - ipX_addr_set_any(0, &pcb->local_ip); - } - } -#endif - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); - ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, - &pcb->remote_ip); - LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_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 ICACHE_FLASH_ATTR -udp_disconnect(struct udp_pcb *pcb) -{ - /* reset remote address association */ - ipX_addr_set_any(PCB_ISIPV6(pcb), &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 ICACHE_FLASH_ATTR -udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) -{ - /* remember recv() callback and user data */ - pcb->recv.ip4 = 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 ICACHE_FLASH_ATTR -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 * ICACHE_FLASH_ATTR -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 LWIP_IPV6 -/** - * Create a UDP PCB for IPv6. - * - * @return The UDP PCB which was created. NULL if the PCB data structure - * could not be allocated. - * - * @see udp_remove() - */ -struct udp_pcb * ICACHE_FLASH_ATTR -udp_new_ip6(void) -{ - struct udp_pcb *pcb; - pcb = udp_new(); - ip_set_v6(pcb, 1); - return pcb; -} -#endif /* LWIP_IPV6 */ - -#if UDP_DEBUG -/** - * Print UDP header information for debug purposes. - * - * @param udphdr pointer to the udp header in memory. - */ -void ICACHE_FLASH_ATTR -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/third_party/lwip/netif/FILES b/third_party/lwip/netif/FILES deleted file mode 100644 index e28952e..0000000 --- a/third_party/lwip/netif/FILES +++ /dev/null @@ -1,29 +0,0 @@ -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/third_party/lwip/netif/Makefile b/third_party/lwip/netif/Makefile deleted file mode 100644 index f03613f..0000000 --- a/third_party/lwip/netif/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = liblwipnetif.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/third_party/lwip/netif/etharp.c b/third_party/lwip/netif/etharp.c deleted file mode 100644 index da7ec06..0000000 --- a/third_party/lwip/netif/etharp.c +++ /dev/null @@ -1,1414 +0,0 @@ -/** - * @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 - * 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" -#include "lwip/ip6.h" - -#if PPPOE_SUPPORT -#include "netif/ppp_oe.h" -#endif /* PPPOE_SUPPORT */ - -#include - -const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; -const struct eth_addr ethzero = {{0,0,0,0,0,0}}; - -/** The 24-bit IANA multicast OUI is 01-00-5e: */ -#define LL_MULTICAST_ADDR_0 0x01 -#define LL_MULTICAST_ADDR_1 0x00 -#define LL_MULTICAST_ADDR_2 0x5e - -#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 -/** Re-request a used ARP entry 1 minute before it would expire to prevent - * breaking a steadily used connection because the ARP entry timed out. */ -#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12) - -/** 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, - ETHARP_STATE_STABLE_REREQUESTING -#if ETHARP_SUPPORT_STATIC_ENTRIES - ,ETHARP_STATE_STATIC -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ -}; - -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 netif *netif; - struct eth_addr ethaddr; - u8_t state; - u8_t ctime; -}; - -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 -#if ETHARP_SUPPORT_STATIC_ENTRIES -#define ETHARP_FLAG_STATIC_ENTRY 4 -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ - -#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 */ - - -/* 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 ICACHE_FLASH_ATTR -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 ICACHE_FLASH_ATTR -etharp_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_free_entry: 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; -#ifdef LWIP_DEBUG - /* for debugging, clean out the complete entry */ - arp_table[i].ctime = 0; - arp_table[i].netif = NULL; - 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 ICACHE_FLASH_ATTR -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 - && (state != ETHARP_STATE_STATIC) -#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 */ - etharp_free_entry(i); - } - else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { - /* Reset state to stable, so that the next transmitted packet will - re-send an ARP request. */ - arp_table[i].state = ETHARP_STATE_STABLE; - } -#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 ICACHE_FLASH_ATTR -etharp_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, ("etharp_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, ("etharp_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 (state < ETHARP_STATE_STATIC) -#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, ("etharp_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, ("etharp_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, ("etharp_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, ("etharp_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 etharp_free_entry) */ - i = old_queue; - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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, ("etharp_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); - etharp_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; - 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 ICACHE_FLASH_ATTR -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(ðhdr->dest, dst); - ETHADDR16_COPY(ðhdr->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 ICACHE_FLASH_ATTR -etharp_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, ("etharp_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, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); - return ERR_ARG; - } - /* find or create ARP entry */ - i = etharp_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].state = ETHARP_STATE_STATIC; - } else -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ - { - /* mark it stable */ - arp_table[i].state = ETHARP_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(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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 ICACHE_FLASH_ATTR -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 etharp_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 ICACHE_FLASH_ATTR -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 = etharp_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_STATIC) { - /* entry wasn't a static entry, cannot remove it */ - return ERR_ARG; - } - /* entry found, free it */ - etharp_free_entry(i); - return ERR_OK; -} -#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ - -/** - * Remove all ARP table entries of the specified netif. - * - * @param netif points to a network interface - */ -void ICACHE_FLASH_ATTR -etharp_cleanup_netif(struct netif *netif) -{ - u8_t i; - - for (i = 0; i < ARP_TABLE_SIZE; ++i) { - u8_t state = arp_table[i].state; - if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { - etharp_free_entry(i); - } - } -} - -/** - * 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 ICACHE_FLASH_ATTR -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 = etharp_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 ICACHE_FLASH_ATTR -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. */ - etharp_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 ICACHE_FLASH_ATTR -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 */ - etharp_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(ðhdr->dest, ethdst_hwaddr); -#else /* LWIP_AUTOIP */ - ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr); -#endif /* LWIP_AUTOIP */ - ETHADDR16_COPY(&hdr->shwaddr, ethaddr); - ETHADDR16_COPY(ðhdr->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); -} - -/** Just a small helper function that sends a pbuf to an ethernet address - * in the arp_table specified by the index 'arp_idx'. - */ -static err_t ICACHE_FLASH_ATTR -etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) -{ - LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", - arp_table[arp_idx].state >= ETHARP_STATE_STABLE); - /* if arp table entry is about to expire: re-request it, - but only if its state is ETHARP_STATE_STABLE to prevent flooding the - network with ARP requests if this address is used frequently. */ - if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && - (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { - if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { - arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; - } - } - - return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), - &arp_table[arp_idx].ethaddr); -} - -/** - * 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 ICACHE_FLASH_ATTR -etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) -{ - struct eth_addr *dest; - struct eth_addr mcastaddr; - ip_addr_t *dst_addr = ipaddr; - - LWIP_ASSERT("netif != NULL", netif != NULL); - LWIP_ASSERT("q != NULL", q != NULL); - LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); - - /* 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; - } - - /* 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 *)ðbroadcast; - /* multicast destination IP address? */ - } else if (ip_addr_ismulticast(ipaddr)) { - /* Hash IP multicast address to MAC address.*/ - mcastaddr.addr[0] = LL_MULTICAST_ADDR_0; - mcastaddr.addr[1] = LL_MULTICAST_ADDR_1; - mcastaddr.addr[2] = LL_MULTICAST_ADDR_2; - 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 { - s8_t i; - /* outside local network? if so, this can neither be a global broadcast nor - a subnet broadcast. */ - 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 */ - dst_addr = &(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(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { - /* the per-pcb-cached entry is stable and the right one! */ - ETHARP_STATS_INC(etharp.cachehit); - return etharp_output_to_arp_index(netif, q, etharp_cached_entry); - } -#if LWIP_NETIF_HWADDRHINT - } - } -#endif /* LWIP_NETIF_HWADDRHINT */ - - /* find stable entry: do this here since this is a critical path for - throughput and etharp_find_entry() is kind of slow */ - for (i = 0; i < ARP_TABLE_SIZE; i++) { - if ((arp_table[i].state >= ETHARP_STATE_STABLE) && - (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { - /* found an existing, stable entry */ - ETHARP_SET_HINT(netif, i); - return etharp_output_to_arp_index(netif, q, i); - } - } - /* no stable entry found, use the (slower) query function: - queue on destination Ethernet address belonging to ipaddr */ - return etharp_query(netif, dst_addr, 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 ICACHE_FLASH_ATTR -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 = etharp_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 ICACHE_FLASH_ATTR -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 */ - - LWIP_ASSERT("netif != NULL", netif != NULL); - - /* 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(ðhdr->dest, ethdst_hwaddr); -#else /* LWIP_AUTOIP */ - ETHADDR16_COPY(ðhdr->dest, ethdst_addr); -#endif /* LWIP_AUTOIP */ - ETHADDR16_COPY(ðhdr->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 ICACHE_FLASH_ATTR -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, ðbroadcast, - (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, - 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 ICACHE_FLASH_ATTR -ethernet_input(struct pbuf *p, struct netif *netif) -{ - struct eth_hdr* ethhdr; - u16_t type; -#if LWIP_ARP || ETHARP_SUPPORT_VLAN - s16_t ip_hdr_offset = SIZEOF_ETH_HDR; -#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ - - 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; - } -#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ -#ifdef ETHARP_VLAN_CHECK_FN - if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { -#elif defined(ETHARP_VLAN_CHECK) - if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { -#endif - /* silently ignore this packet: not for our VLAN */ - pbuf_free(p); - return ERR_OK; - } -#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ - 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*/ - - if (ethhdr->dest.addr[0] & 1) { - /* this might be a multicast or broadcast packet */ - if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) { - if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) && - (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) { - /* mark the pbuf as link-layer multicast */ - p->flags |= PBUF_FLAG_LLMCAST; - } - } else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { - /* mark the pbuf as link-layer broadcast */ - p->flags |= PBUF_FLAG_LLBCAST; - } - } - - 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 */ - -#if LWIP_IPV6 - case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ - /* skip Ethernet header */ - if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { - LWIP_ASSERT("Can't move over header in packet", 0); - goto free_and_return; - } else { - /* pass to IPv6 layer */ - ip6_input(p, netif); - } - break; -#endif /* LWIP_IPV6 */ - - 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/third_party/lwip/netif/ethernetif.c b/third_party/lwip/netif/ethernetif.c deleted file mode 100644 index 5f6d5f8..0000000 --- a/third_party/lwip/netif/ethernetif.c +++ /dev/null @@ -1,220 +0,0 @@ -/** - * @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 - * - */ - -#include "lwip/opt.h" - -#include "lwip/def.h" -#include "lwip/mem.h" -#include "lwip/pbuf.h" -#include "lwip/stats.h" -#include "lwip/snmp.h" -#include "lwip/ethip6.h" -#include "netif/etharp.h" -#include "netif/ppp_oe.h" - -/* Define those to better describe your network interface. */ -#define IFNAME0 'e' -#define IFNAME1 'n' - -/** - * 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 ICACHE_FLASH_ATTR -low_level_init(struct netif *netif) -{ - /* set MAC hardware address length */ - netif->hwaddr_len = ETHARP_HWADDR_LEN; - - /* set MAC hardware address */ - - /* 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 ICACHE_FLASH_ATTR -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 - - struct pbuf *tmp; - 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); - tmp = q->next; - - ieee80211_output_pbuf(netif, q); - q->next = tmp; - } - -// 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; -} - -/** - * 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 - */ -void ICACHE_FLASH_ATTR -ethernetif_input(struct netif *netif, struct pbuf *p) -{ - struct ethernetif *ethernetif; - struct eth_hdr *ethhdr; - - ethernetif = netif->state; - - /* 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_IPV6: - 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 ICACHE_FLASH_ATTR -ethernetif_init(struct netif *netif) -{ - LWIP_ASSERT("netif != NULL", (netif != NULL)); - -#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->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; -#if LWIP_IPV6 - netif->output_ip6 = ethip6_output; -#endif /* LWIP_IPV6 */ - netif->linkoutput = low_level_output; - - /* initialize the hardware */ - low_level_init(netif); - - return ERR_OK; -} diff --git a/third_party/lwip/netif/ppp/auth.c b/third_party/lwip/netif/ppp/auth.c deleted file mode 100644 index 0b0885d..0000000 --- a/third_party/lwip/netif/ppp/auth.c +++ /dev/null @@ -1,1334 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-08 Guy Lancaster , 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_impl.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 - -#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 = os_strlen(user); - if (l > 0 && user[l-1] == '\n') - user[l-1] = 0; - l = os_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 = os_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 = os_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 = os_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] || os_strcmp(client, ppp_settings.user)) { - return 0; - } - - len = (int)os_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 = os_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/third_party/lwip/netif/ppp/auth.h b/third_party/lwip/netif/ppp/auth.h deleted file mode 100644 index 593e17d..0000000 --- a/third_party/lwip/netif/ppp/auth.h +++ /dev/null @@ -1,111 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-04 Guy Lancaster , 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/third_party/lwip/netif/ppp/chap.c b/third_party/lwip/netif/ppp/chap.c deleted file mode 100644 index 464dc4e..0000000 --- a/third_party/lwip/netif/ppp/chap.c +++ /dev/null @@ -1,908 +0,0 @@ -/*** 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 -* Ported to lwIP. -* 97-12-04 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include "magic.h" -#include "randm.h" -#include "auth.h" -#include "md5.h" -#include "chap.h" -#include "chpms.h" - -#include - -#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)os_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)os_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)os_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/third_party/lwip/netif/ppp/chap.h b/third_party/lwip/netif/ppp/chap.h deleted file mode 100644 index 9591c95..0000000 --- a/third_party/lwip/netif/ppp/chap.h +++ /dev/null @@ -1,150 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-03 Guy Lancaster , 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/third_party/lwip/netif/ppp/chpms.c b/third_party/lwip/netif/ppp/chpms.c deleted file mode 100644 index d039f05..0000000 --- a/third_party/lwip/netif/ppp/chpms.c +++ /dev/null @@ -1,396 +0,0 @@ -/*** 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 -* Ported to lwIP. -* 97-12-08 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include "md4.h" -#ifndef USE_CRYPT -#include "des.h" -#endif -#include "chap.h" -#include "chpms.h" - -#include - - -/*************************/ -/*** 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/third_party/lwip/netif/ppp/chpms.h b/third_party/lwip/netif/ppp/chpms.h deleted file mode 100644 index f023cce..0000000 --- a/third_party/lwip/netif/ppp/chpms.h +++ /dev/null @@ -1,64 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 98-01-30 Guy Lancaster , 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/third_party/lwip/netif/ppp/fsm.c b/third_party/lwip/netif/ppp/fsm.c deleted file mode 100644 index 167861d..0000000 --- a/third_party/lwip/netif/ppp/fsm.c +++ /dev/null @@ -1,890 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-01 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include "fsm.h" - -#include - -#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)os_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/third_party/lwip/netif/ppp/fsm.h b/third_party/lwip/netif/ppp/fsm.h deleted file mode 100644 index 9bc7832..0000000 --- a/third_party/lwip/netif/ppp/fsm.h +++ /dev/null @@ -1,157 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-11-05 Guy Lancaster , 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/third_party/lwip/netif/ppp/ipcp.c b/third_party/lwip/netif/ppp/ipcp.c deleted file mode 100644 index d9aee84..0000000 --- a/third_party/lwip/netif/ppp/ipcp.c +++ /dev/null @@ -1,1411 +0,0 @@ -/** 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 -* Ported to lwIP. -* 97-12-08 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include "auth.h" -#include "fsm.h" -#include "vj.h" -#include "ipcp.h" - -#include "lwip/inet.h" - -#include - -/* #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/third_party/lwip/netif/ppp/ipcp.h b/third_party/lwip/netif/ppp/ipcp.h deleted file mode 100644 index 9ef748e..0000000 --- a/third_party/lwip/netif/ppp/ipcp.h +++ /dev/null @@ -1,106 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-04 Guy Lancaster , 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/third_party/lwip/netif/ppp/lcp.c b/third_party/lwip/netif/ppp/lcp.c deleted file mode 100644 index b551a04..0000000 --- a/third_party/lwip/netif/ppp/lcp.c +++ /dev/null @@ -1,2066 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-01 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include "fsm.h" -#include "chap.h" -#include "magic.h" -#include "auth.h" -#include "lcp.h" - -#include - -#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/third_party/lwip/netif/ppp/lcp.h b/third_party/lwip/netif/ppp/lcp.h deleted file mode 100644 index 49478c8..0000000 --- a/third_party/lwip/netif/ppp/lcp.h +++ /dev/null @@ -1,151 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-11-05 Guy Lancaster , 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/third_party/lwip/netif/ppp/magic.c b/third_party/lwip/netif/ppp/magic.c deleted file mode 100644 index 0c53c83..0000000 --- a/third_party/lwip/netif/ppp/magic.c +++ /dev/null @@ -1,80 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-04 Guy Lancaster , 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_impl.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/third_party/lwip/netif/ppp/magic.h b/third_party/lwip/netif/ppp/magic.h deleted file mode 100644 index e308037..0000000 --- a/third_party/lwip/netif/ppp/magic.h +++ /dev/null @@ -1,63 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-04 Guy Lancaster , 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/third_party/lwip/netif/ppp/md5.c b/third_party/lwip/netif/ppp/md5.c deleted file mode 100644 index befbbab..0000000 --- a/third_party/lwip/netif/ppp/md5.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - *********************************************************************** - ** 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_impl.h" -#include "pppdebug.h" - -#include "md5.h" - -#include - -/* - *********************************************************************** - ** 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/third_party/lwip/netif/ppp/md5.h b/third_party/lwip/netif/ppp/md5.h deleted file mode 100644 index 0bcb23c..0000000 --- a/third_party/lwip/netif/ppp/md5.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - *********************************************************************** - ** 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/third_party/lwip/netif/ppp/pap.c b/third_party/lwip/netif/ppp/pap.c deleted file mode 100644 index 7436544..0000000 --- a/third_party/lwip/netif/ppp/pap.c +++ /dev/null @@ -1,628 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-12 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include "auth.h" -#include "pap.h" - -#include - -#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)os_strlen(user); - u->us_passwd = password; - u->us_passwdlen = (int)os_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/third_party/lwip/netif/ppp/pap.h b/third_party/lwip/netif/ppp/pap.h deleted file mode 100644 index 4d1c424..0000000 --- a/third_party/lwip/netif/ppp/pap.h +++ /dev/null @@ -1,118 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-12-04 Guy Lancaster , 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/third_party/lwip/netif/ppp/ppp.c b/third_party/lwip/netif/ppp/ppp.c deleted file mode 100644 index ca50a88..0000000 --- a/third_party/lwip/netif/ppp/ppp.c +++ /dev/null @@ -1,2052 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-11-05 Guy Lancaster , 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 "ppp_impl.h" -#include "lwip/ip.h" /* for ip_input() */ - -#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 - -/*************************/ -/*** 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 */ -#if PPP_INPROC_OWNTHREAD - u_char rxbuf[PPPOS_RX_BUFSIZE]; -#endif /* PPP_INPROC_OWNTHREAD */ - - /* 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); -static void pppFreeCurrentInputPacket(PPPControlRx *pcrx); -#endif /* PPPOS_SUPPORT */ - - -/******************************/ -/*** PUBLIC DATA STRUCTURES ***/ -/******************************/ -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 -}; - -#if PPP_INPROC_OWNTHREAD -/** 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 /* PPP_INPROC_OWNTHREAD */ -#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; -#if PPP_INPROC_OWNTHREAD - pppRecvWakeup(pd); -#endif /* PPP_INPROC_OWNTHREAD */ - 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 && PPP_INPROC_OWNTHREAD - pppRecvWakeup(pd); -#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/ - } -} - -/** 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(); - - 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, pppLinkStatusCB_fn linkStatusCB, 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]; - /* input pbuf left over from last session? */ - pppFreeCurrentInputPacket(&pc->rx); - /* @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 /* PPP_INPROC_OWNTHREAD */ - } - - 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, - pppLinkStatusCB_fn linkStatusCB, 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); -#if PPP_INPROC_OWNTHREAD - pppRecvWakeup(pd); -#endif /* PPP_INPROC_OWNTHREAD */ -#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; - - addr = 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 = PP_HTONL(0xffffff00UL) | 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; -} - -/** Input helper struct, must be packed since it is stored to pbuf->payload, - * which might be unaligned. - */ -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/bpstruct.h" -#endif -PACK_STRUCT_BEGIN -struct pppInputHeader { - PACK_STRUCT_FIELD(int unit); - PACK_STRUCT_FIELD(u16_t proto); -} PACK_STRUCT_STRUCT; -PACK_STRUCT_END -#ifdef PACK_STRUCT_USE_INCLUDES -# include "arch/epstruct.h" -#endif - -/* - * 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 -pppFreeCurrentInputPacket(PPPControlRx *pcrx) -{ - if (pcrx->inHead != NULL) { - if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) { - pbuf_free(pcrx->inTail); - } - pbuf_free(pcrx->inHead); - pcrx->inHead = NULL; - } - pcrx->inTail = NULL; -} - -/* - * Drop the input packet and increase error counters. - */ -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)); - } - pppFreeCurrentInputPacket(pcrx); -#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); -} - -#if !PPP_INPROC_OWNTHREAD -/** 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); -} -#endif - -/** - * 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/third_party/lwip/netif/ppp/ppp.h b/third_party/lwip/netif/ppp/ppp.h deleted file mode 100644 index f8ec6c3..0000000 --- a/third_party/lwip/netif/ppp/ppp.h +++ /dev/null @@ -1,201 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-11-05 Guy Lancaster , 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" - - -#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 - - -/************************* -*** 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 *** -************************/ - -struct ppp_addrs { - ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2; -}; - - -/*********************** -*** 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); - -/* Link status callback function prototype */ -typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg); - -#if PPPOS_SUPPORT -/* - * 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, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); -#endif /* PPPOS_SUPPORT */ - -#if PPPOE_SUPPORT -/* - * Open a new PPP Over Ethernet (PPPOE) connection. - */ -int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, - pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); -#endif /* PPPOE_SUPPORT */ - -/* 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); - -#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD -/* - * PPP over Serial: this is the input function to be called for received data. - * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking - * sio_read() is used, so this is deactivated. - */ -void pppos_input(int pd, u_char* data, int len); -#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */ - - -#if LWIP_NETIF_STATUS_CALLBACK -/* Set an lwIP-style status-callback for the selected PPP device */ -void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback); -#endif /* LWIP_NETIF_STATUS_CALLBACK */ -#if LWIP_NETIF_LINK_CALLBACK -/* Set an lwIP-style link-callback for the selected PPP device */ -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/third_party/lwip/netif/ppp/ppp_impl.h b/third_party/lwip/netif/ppp/ppp_impl.h deleted file mode 100644 index aadfc4e..0000000 --- a/third_party/lwip/netif/ppp/ppp_impl.h +++ /dev/null @@ -1,363 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 97-11-05 Guy Lancaster , Global Election Systems Inc. -* Original derived from BSD codes. -*****************************************************************************/ - -#ifndef PPP_IMPL_H -#define PPP_IMPL_H - -#include "lwip/opt.h" - -#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ - -#include "ppp.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)) - - -/* - * 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 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 */ -}; - -/***************************** -*** 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 *** -***********************/ - -/* - * 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); - -/* 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); - -#endif /* PPP_SUPPORT */ - -#endif /* PPP_IMPL_H */ diff --git a/third_party/lwip/netif/ppp/ppp_oe.c b/third_party/lwip/netif/ppp/ppp_oe.c deleted file mode 100644 index 9718bcb..0000000 --- a/third_party/lwip/netif/ppp/ppp_oe.c +++ /dev/null @@ -1,1132 +0,0 @@ -/***************************************************************************** -* 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 -* 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 . - * - * 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_impl.h" -#include "pppdebug.h" - -#include "lwip/timers.h" -#include "lwip/memp.h" - -#include -#include - - -/* 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/third_party/lwip/netif/ppp/pppdebug.h b/third_party/lwip/netif/ppp/pppdebug.h deleted file mode 100644 index 08ba7b5..0000000 --- a/third_party/lwip/netif/ppp/pppdebug.h +++ /dev/null @@ -1,73 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 98-07-29 Guy Lancaster , 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/third_party/lwip/netif/ppp/randm.c b/third_party/lwip/netif/ppp/randm.c deleted file mode 100644 index 09340f3..0000000 --- a/third_party/lwip/netif/ppp/randm.c +++ /dev/null @@ -1,249 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 98-06-03 Guy Lancaster , 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_impl.h" -#include "pppdebug.h" - -#include - -#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/third_party/lwip/netif/ppp/randm.h b/third_party/lwip/netif/ppp/randm.h deleted file mode 100644 index 5fb99ef..0000000 --- a/third_party/lwip/netif/ppp/randm.h +++ /dev/null @@ -1,81 +0,0 @@ -/***************************************************************************** -* 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 -* Ported to lwIP. -* 98-05-29 Guy Lancaster , 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/third_party/lwip/netif/ppp/readme.txt b/third_party/lwip/netif/ppp/readme.txt deleted file mode 100644 index 1895dc6..0000000 --- a/third_party/lwip/netif/ppp/readme.txt +++ /dev/null @@ -1,21 +0,0 @@ -About the PPP code: - -The PPP code is not our "own" code - we just copied it from pppd (http://ppp.samba.org/) and adapted it to lwIP. -Unfortunately, not many here know their way around it too well. Back in 2009, we took the effort to see which -version of pppd our code relates to and we're pretty much on 2.3.11 with some bugs from 2.4.x backported. - -Aside from simple code adaptions, there are some files that are different, however: -- chpms.c/.h are named chap_ms.c/.h in the original pppd 2.3.11 sources -- pap.c/.h are named upap.c/.h in the original pppd 2.3.11 sources -- randm.c is a random generator not included in the original pppd -- magic.c does not use the C library's random functions, but uses randm.c instead -- vj.c/.h is an implementation of the Van Jacobson header compression algorithm adapted to lwIP pbufs, - probably copied from one of the vjcompress.c files from pppd. -- ppp.c, ppp.h and ppp_impl.h contain the adaption from pppd to lwIP. This is the "OS"-dependent part like there - is an implementation for linux, xBSD etc. in the pppd sources. -- ppp_oe.c is Marc Boucher's implementation based on NetBSD's if_pppoe.c - -There is of course potential for bugs in it, but when analyzing of reporting bugs, it is strongly encouraged to -compare the code in question to pppd 2.3.11 (our basis) and newer versions (perhaps it's already fixed?) and to -share this knowledge with us when reporting a bug. - diff --git a/third_party/lwip/netif/ppp/vj.c b/third_party/lwip/netif/ppp/vj.c deleted file mode 100644 index 6bed149..0000000 --- a/third_party/lwip/netif/ppp/vj.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - * 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_impl.h" -#include "pppdebug.h" - -#include "vj.h" - -#include - -#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_HDRLEN(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_HDRLEN(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_HDRLEN(th) != TCPH_HDRLEN(oth) - || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) - || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(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_HDRLEN(((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/third_party/lwip/netif/ppp/vj.h b/third_party/lwip/netif/ppp/vj.h deleted file mode 100644 index 373fd93..0000000 --- a/third_party/lwip/netif/ppp/vj.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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/third_party/lwip/netif/slipif.c b/third_party/lwip/netif/slipif.c deleted file mode 100644 index a73f6bf..0000000 --- a/third_party/lwip/netif/slipif.c +++ /dev/null @@ -1,546 +0,0 @@ -/** - * @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 - * Simon Goldschmidt - * - * Usage: This netif can be used in three ways: - * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() - * until data is received. - * 2) In your main loop, call slipif_poll() to check for new RX bytes, - * completed packets are fed into netif->input(). - * 3) Call slipif_received_byte[s]() from your serial RX ISR and - * slipif_process_rxqueue() from your main loop. ISR level decodes - * packets and puts completed packets on a queue which is fed into - * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for - * pbuf_alloc to work on ISR level!). - * - */ - -/* - * 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/stats.h" -#include "lwip/snmp.h" -#include "lwip/sio.h" -#include "lwip/sys.h" - -#define SLIP_END 0xC0 /* 0300: start and end of every packet */ -#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ -#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ -#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ - -/** Maximum packet size that is received by this netif */ -#ifndef SLIP_MAX_SIZE -#define SLIP_MAX_SIZE 1500 -#endif - -/** Define this to the interface speed for SNMP - * (sio_fd is the sio_fd_t returned by sio_open). - * The default value of zero means 'unknown'. - */ -#ifndef SLIP_SIO_SPEED -#define SLIP_SIO_SPEED(sio_fd) 0 -#endif - -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; - u8_t state; - u16_t i, recved; -#if SLIP_RX_FROM_ISR - struct pbuf *rxpackets; -#endif -}; - -/** - * 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 - * @return always returns ERR_OK since the serial layer does not provide return values - */ -static err_t ICACHE_FLASH_ATTR -slipif_output(struct netif *netif, struct pbuf *p) -{ - 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_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); - priv = netif->state; - - /* Send pbuf out on the serial I/O device. */ - /* Start with packet delimiter. */ - 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: - /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ - sio_send(SLIP_ESC, priv->sd); - sio_send(SLIP_ESC_END, priv->sd); - break; - case SLIP_ESC: - /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ - sio_send(SLIP_ESC, priv->sd); - sio_send(SLIP_ESC_ESC, priv->sd); - break; - default: - /* normal byte - no need for escaping */ - sio_send(c, priv->sd); - break; - } - } - } - /* End with packet delimiter. */ - sio_send(SLIP_END, priv->sd); - return ERR_OK; -} - -/** - * 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 - */ -static err_t ICACHE_FLASH_ATTR -slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) -{ - LWIP_UNUSED_ARG(ipaddr); - return slipif_output(netif, p); -} - -#if LWIP_IPV6 -/** - * 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 - */ -static err_t ICACHE_FLASH_ATTR -slipif_output_v6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) -{ - LWIP_UNUSED_ARG(ipaddr); - return slipif_output(netif, p); -} -#endif /* LWIP_IPV6 */ - -/** - * Handle the incoming SLIP stream character by character - * - * @param netif the lwip network interface structure for this slipif - * @param c received character (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* ICACHE_FLASH_ATTR -slipif_rxbyte(struct netif *netif, u8_t c) -{ - struct slipif_priv *priv; - struct pbuf *t; - - LWIP_ASSERT("netif != NULL", (netif != NULL)); - LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); - - priv = netif->state; - - 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 (%"U16_F" bytes)\n", priv->recved)); - t = priv->q; - priv->p = priv->q = NULL; - priv->i = priv->recved = 0; - return t; - } - return NULL; - case SLIP_ESC: - priv->state = SLIP_RECV_ESCAPE; - return NULL; - } /* end switch (c) */ - break; - case SLIP_RECV_ESCAPE: - /* un-escape END or ESC bytes, leave other bytes - (although that would be a protocol error) */ - switch (c) { - case SLIP_ESC_END: - c = SLIP_END; - break; - case SLIP_ESC_ESC: - c = SLIP_ESC; - break; - } - priv->state = SLIP_RECV_NORMAL; - break; - } /* end switch (priv->state) */ - - /* 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 */ - return NULL; - } - - 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; -} - -/** Like slipif_rxbyte, but passes completed packets to netif->input - * - * @param netif The lwip network interface structure for this slipif - * @param data received character - */ -static void ICACHE_FLASH_ATTR -slipif_rxbyte_input(struct netif *netif, u8_t c) -{ - struct pbuf *p; - p = slipif_rxbyte(netif, c); - if (p != NULL) { - if (netif->input(p, netif) != ERR_OK) { - pbuf_free(p); - } - } -} - -#if SLIP_USE_RX_THREAD -/** - * The SLIP input thread. - * - * Feed the IP layer with incoming packets - * - * @param nf the lwip network interface structure for this slipif - */ -static void ICACHE_FLASH_ATTR -slipif_loop_thread(void *nf) -{ - u8_t c; - struct netif *netif = (struct netif *)nf; - struct slipif_priv *priv = (struct slipif_priv *)netif->state; - - while (1) { - if (sio_read(priv->sd, &c, 1) > 0) { - slipif_rxbyte_input(netif, c); - } - } -} -#endif /* SLIP_USE_RX_THREAD */ - -/** - * 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). If netif->state is != NULL, it is interpreted as an - * u8_t pointer pointing to the serial port number instead of netif->num. - * - */ -err_t ICACHE_FLASH_ATTR -slipif_init(struct netif *netif) -{ - struct slipif_priv *priv; - u8_t sio_num; - - LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); - - /* Allocate private data */ - priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); - if (!priv) { - return ERR_MEM; - } - - netif->name[0] = 's'; - netif->name[1] = 'l'; - netif->output = slipif_output_v4; -#if LWIP_IPV6 - netif->output_ip6 = slipif_output_v6; -#endif /* LWIP_IPV6 */ - netif->mtu = SLIP_MAX_SIZE; - netif->flags |= NETIF_FLAG_POINTTOPOINT; - - /* netif->state or netif->num contain the port number */ - if (netif->state != NULL) { - sio_num = *(u8_t*)netif->state; - } else { - sio_num = netif->num; - } - /* Try to open the serial port. */ - priv->sd = sio_open(sio_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; -#if SLIP_RX_FROM_ISR - priv->rxpackets = NULL; -#endif - - netif->state = priv; - - /* initialize the snmp variables and counters inside the struct netif */ - NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); - -#if SLIP_USE_RX_THREAD - /* Create a thread to poll the serial line. */ - sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, - SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); -#endif /* SLIP_USE_RX_THREAD */ - 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 ICACHE_FLASH_ATTR -slipif_poll(struct netif *netif) -{ - u8_t c; - struct slipif_priv *priv; - - LWIP_ASSERT("netif != NULL", (netif != NULL)); - LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); - - priv = (struct slipif_priv *)netif->state; - - while (sio_tryread(priv->sd, &c, 1) > 0) { - slipif_rxbyte_input(netif, c); - } -} - -#if SLIP_RX_FROM_ISR -/** - * Feeds the IP layer with incoming packets that were receive - * - * @param netif The lwip network interface structure for this slipif - */ -void ICACHE_FLASH_ATTR -slipif_process_rxqueue(struct netif *netif) -{ - struct slipif_priv *priv; - SYS_ARCH_DECL_PROTECT(old_level); - - LWIP_ASSERT("netif != NULL", (netif != NULL)); - LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); - - priv = (struct slipif_priv *)netif->state; - - SYS_ARCH_PROTECT(old_level); - while (priv->rxpackets != NULL) { - struct pbuf *p = priv->rxpackets; -#if SLIP_RX_QUEUE - /* dequeue packet */ - struct pbuf *q = p; - while ((q->len != q->tot_len) && (q->next != NULL)) { - q = q->next; - } - priv->rxpackets = q->next; - q->next = NULL; -#else /* SLIP_RX_QUEUE */ - priv->rxpackets = NULL; -#endif /* SLIP_RX_QUEUE */ - SYS_ARCH_UNPROTECT(old_level); - if (netif->input(p, netif) != ERR_OK) { - pbuf_free(p); - } - SYS_ARCH_PROTECT(old_level); - } -} - -/** Like slipif_rxbyte, but queues completed packets. - * - * @param netif The lwip network interface structure for this slipif - * @param data Received serial byte - */ -static void ICACHE_FLASH_ATTR -slipif_rxbyte_enqueue(struct netif *netif, u8_t data) -{ - struct pbuf *p; - struct slipif_priv *priv = (struct slipif_priv *)netif->state; - SYS_ARCH_DECL_PROTECT(old_level); - - p = slipif_rxbyte(netif, data); - if (p != NULL) { - SYS_ARCH_PROTECT(old_level); - if (priv->rxpackets != NULL) { -#if SLIP_RX_QUEUE - /* queue multiple pbufs */ - struct pbuf *q = p; - while(q->next != NULL) { - q = q->next; - } - q->next = p; - } else { -#else /* SLIP_RX_QUEUE */ - pbuf_free(priv->rxpackets); - } - { -#endif /* SLIP_RX_QUEUE */ - priv->rxpackets = p; - } - SYS_ARCH_UNPROTECT(old_level); - } -} - -/** - * Process a received byte, completed packets are put on a queue that is - * fed into IP through slipif_process_rxqueue(). - * - * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. - * - * @param netif The lwip network interface structure for this slipif - * @param data received character - */ -void ICACHE_FLASH_ATTR -slipif_received_byte(struct netif *netif, u8_t data) -{ - LWIP_ASSERT("netif != NULL", (netif != NULL)); - LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); - slipif_rxbyte_enqueue(netif, data); -} - -/** - * Process multiple received byte, completed packets are put on a queue that is - * fed into IP through slipif_process_rxqueue(). - * - * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. - * - * @param netif The lwip network interface structure for this slipif - * @param data received character - * @param len Number of received characters - */ -void ICACHE_FLASH_ATTR -slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) -{ - u8_t i; - u8_t *rxdata = data; - LWIP_ASSERT("netif != NULL", (netif != NULL)); - LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); - - for (i = 0; i < len; i++, rxdata++) { - slipif_rxbyte_enqueue(netif, *rxdata); - } -} -#endif /* SLIP_RX_FROM_ISR */ - -#endif /* LWIP_HAVE_SLIPIF */ diff --git a/tools/gen_appbin.py b/tools/gen_appbin.py index 66019d1..d562901 100644 --- a/tools/gen_appbin.py +++ b/tools/gen_appbin.py @@ -1,71 +1,245 @@ #!/usr/bin/python # -# Copyright (c) 2010 Espressif System +# File : gen_appbin.py +# This file is part of Espressif's generate bin script. +# Copyright (C) 2013 - 2016, Espressif Systems # -# grab user_start() address and pass it to genflashbin program +# This program is free software: you can redistribute it and/or modify +# it under the terms of version 3 of the GNU General Public License as +# published by the Free Software Foundation. # +# 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, see . + +"""This file is part of Espressif's generate bin script. + argv[1] is elf file name + argv[2] is version num""" import string import sys import os import re +import binascii +import struct + + +TEXT_ADDRESS = 0x40100000 +# app_entry = 0 +# data_address = 0x3ffb0000 +# data_end = 0x40000000 +# text_end = 0x40120000 + +CHECKSUM_INIT = 0xEF + +chk_sum = CHECKSUM_INIT +blocks = 0 + +def write_file(file_name,data): + if file_name is None: + print 'file_name cannot be none\n' + sys.exit(0) + + fp = open(file_name,'ab') + + if fp: + fp.seek(0,os.SEEK_END) + fp.write(data) + fp.close() + else: + print '%s write fail\n'%(file_name) + +def combine_bin(file_name,dest_file_name,start_offset_addr,need_chk): + global chk_sum + global blocks + if dest_file_name is None: + print 'dest_file_name cannot be none\n' + sys.exit(0) + + if file_name: + fp = open(file_name,'rb') + if fp: + ########## write text ########## + fp.seek(0,os.SEEK_END) + data_len = fp.tell() + if data_len: + if need_chk: + tmp_len = (data_len + 3) & (~3) + else: + tmp_len = (data_len + 15) & (~15) + data_bin = struct.pack(' eagle.app.sym' + else : + cmd = 'xt-nm -g ' + elf_file + ' > eagle.app.sym' + + os.system(cmd) + + fp = file('./eagle.app.sym') + if fp is None: + print "open sym file error\n" + sys.exit(0) + + lines = fp.readlines() + fp.close() + + entry_addr = None + p = re.compile('(\w*)(\sT\s)(call_user_start)$') + for line in lines: + m = p.search(line) + if m != None: + entry_addr = m.group(1) + # print entry_addr + + if entry_addr is None: + print 'no entry point!!' + sys.exit(0) + + data_start_addr = '0' + p = re.compile('(\w*)(\sA\s)(_data_start)$') + for line in lines: + m = p.search(line) + if m != None: + data_start_addr = m.group(1) + # print data_start_addr + + rodata_start_addr = '0' + p = re.compile('(\w*)(\sA\s)(_rodata_start)$') + for line in lines: + m = p.search(line) + if m != None: + rodata_start_addr = m.group(1) + # print rodata_start_addr + + # write flash bin header + #============================ + # SPI FLASH PARAMS + #------------------- + #flash_mode= + # 0: QIO + # 1: QOUT + # 2: DIO + # 3: DOUT + #------------------- + #flash_clk_div= + # 0 : 80m / 2 + # 1 : 80m / 3 + # 2 : 80m / 4 + # 0xf: 80m / 1 + #------------------- + #flash_size= + # 0 : 512 KB + # 1 : 256 KB + # 2 : 1024 KB + # 3 : 2048 KB + # 4 : 4096 KB + #------------------- + # END OF SPI FLASH PARAMS + #============================ + byte2=int(flash_mode)&0xff + byte3=(((int(flash_size)<<4)| int(flash_clk_div))&0xff) + + if boot_mode == '2': + # write irom bin head + data_bin = struct.pack(' eagle.app.sym' -#print cmd -os.system(cmd) - -fp = file('./eagle.app.sym') -if fp is None: - print "open sym file error\n" - exit - -lines = fp.readlines() - -fp.close() - -entry_addr = None -p = re.compile('(\w*)(\sT\s)(call_user_start)$') -for line in lines: - m = p.search(line) - if m != None: - entry_addr = m.group(1) - #entry_addr = int(entry_addr, 16) - print entry_addr - -if entry_addr is None: - print 'no entry point!!' - exit - -data_start_addr = '0' -p = re.compile('(\w*)(\sA\s)(_data_start)$') -for line in lines: - m = p.search(line) - if m != None: - data_start_addr = m.group(1) - print data_start_addr - -rodata_start_addr = '0' -p = re.compile('(\w*)(\sA\s)(_rodata_start)$') -for line in lines: - m = p.search(line) - if m != None: - rodata_start_addr = m.group(1) - print rodata_start_addr - -cmd = 'genflashbin%s eagle.app.%s.text.bin '%(ver, ver)+entry_addr+' eagle.app.%s.data.bin '%(ver)+ data_start_addr+' eagle.app.%s.rodata.bin '%(ver)+rodata_start_addr - -print cmd -os.system(cmd) - -cmd = 'mv eagle.app.flash.bin eagle.app.%s.flash.bin'%(ver) - -print cmd -os.system(cmd) +if __name__=='__main__': + gen_appbin() diff --git a/tools/gen_flashbin.py b/tools/gen_flashbin.py deleted file mode 100644 index 0f8f92b..0000000 --- a/tools/gen_flashbin.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/python -import os -import sys -import binascii -import string - -if len(sys.argv) != 3: - print 'Usage: gen_flashbin.py 1.bin 2.bin' - sys.exit(0) - -bin1_name = sys.argv[1] -bin2_name = sys.argv[2] - -bin1_file = open(bin1_name, 'rb') -bin2_file = open(bin2_name, 'rb') - -bin1_len = os.path.getsize(bin1_name) - -bin1 = bin1_file.read() -bin2 = bin2_file.read() - -bitlist = ['FF']*(0x10000-bin1_len) -bytes = binascii.a2b_hex(''.join(bitlist)) - -bitout = open('eagle.app.flash.bin', 'wb') -bitout.write(bin1) -bitout.write(bytes) -bitout.write(bin2) -bitout.close() - -bin1_file.close() -bin2_file.close() -