Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cproject9
-rw-r--r--.settings/org.eclipse.cdt.codan.core.prefs5
-rw-r--r--Libraries/EMAC/arch/cc.h (renamed from Libraries/Lwip/lwip/src/sam/include/arch/cc.h)54
-rw-r--r--Libraries/EMAC/arch/perf.h (renamed from Libraries/Lwip/lwip/src/sam/include/arch/perf.h)6
-rw-r--r--Libraries/EMAC/emac.h1226
-rw-r--r--Libraries/EMAC/ethernet_phy.h2
-rw-r--r--Libraries/EMAC/ethernet_sam.c73
-rw-r--r--Libraries/EMAC/ethernet_sam.h48
-rw-r--r--Libraries/EMAC/ethernetif.c (renamed from Libraries/Lwip/lwip/src/sam/netif/ethernetif.c)76
-rw-r--r--Libraries/EMAC/ethernetif.h (renamed from Libraries/Lwip/lwip/src/sam/include/netif/ethernetif.h)0
-rw-r--r--Libraries/EMAC/examples/Arduino_Due_EMAC_Mod/Arduino_Due_EMAC_Mod.ino369
-rw-r--r--Libraries/Lwip/contrib/apps/mdns/mdns_responder.c664
-rw-r--r--Libraries/Lwip/contrib/apps/mdns/mdns_responder.h58
-rw-r--r--Libraries/Lwip/lwip/CHANGELOG299
-rw-r--r--Libraries/Lwip/lwip/COPYING33
-rw-r--r--Libraries/Lwip/lwip/UPGRADING144
-rw-r--r--Libraries/Lwip/lwip/doc/contrib.txt2
-rw-r--r--Libraries/Lwip/lwip/doc/rawapi.txt42
-rw-r--r--Libraries/Lwip/lwip/doc/savannah.txt2
-rw-r--r--Libraries/Lwip/lwip/doc/sys_arch.txt77
-rw-r--r--Libraries/Lwip/lwip/src/api/api_lib.c54
-rw-r--r--Libraries/Lwip/lwip/src/api/api_msg.c144
-rw-r--r--Libraries/Lwip/lwip/src/api/netdb.c19
-rw-r--r--Libraries/Lwip/lwip/src/api/sockets.c77
-rw-r--r--Libraries/Lwip/lwip/src/api/tcpip.c91
-rw-r--r--Libraries/Lwip/lwip/src/core/dhcp.c177
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv4/autoip.c36
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv4/icmp.c4
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv4/igmp.c20
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv4/ip.c90
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv6/README1
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv6/icmp6.c179
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv6/inet6.c163
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv6/ip6.c397
-rw-r--r--Libraries/Lwip/lwip/src/core/ipv6/ip6_addr.c72
-rw-r--r--Libraries/Lwip/lwip/src/core/lwip_init.c (renamed from Libraries/Lwip/lwip/src/core/init.c)0
-rw-r--r--Libraries/Lwip/lwip/src/core/lwip_timers_141.c (renamed from Libraries/Lwip/lwip/src/core/lwip_timers_140.c)6
-rw-r--r--Libraries/Lwip/lwip/src/core/mem.c41
-rw-r--r--Libraries/Lwip/lwip/src/core/memp.c15
-rw-r--r--Libraries/Lwip/lwip/src/core/netif.c67
-rw-r--r--Libraries/Lwip/lwip/src/core/pbuf.c130
-rw-r--r--Libraries/Lwip/lwip/src/core/raw.c14
-rw-r--r--Libraries/Lwip/lwip/src/core/snmp/asn1_dec.c657
-rw-r--r--Libraries/Lwip/lwip/src/core/snmp/asn1_enc.c611
-rw-r--r--Libraries/Lwip/lwip/src/core/snmp/mib2.c4146
-rw-r--r--Libraries/Lwip/lwip/src/core/snmp/mib_structs.c1174
-rw-r--r--Libraries/Lwip/lwip/src/core/snmp/msg_in.c1453
-rw-r--r--Libraries/Lwip/lwip/src/core/snmp/msg_out.c674
-rw-r--r--Libraries/Lwip/lwip/src/core/stats.c106
-rw-r--r--Libraries/Lwip/lwip/src/core/sys.c2
-rw-r--r--Libraries/Lwip/lwip/src/core/tcp.c313
-rw-r--r--Libraries/Lwip/lwip/src/core/tcp_in.c172
-rw-r--r--Libraries/Lwip/lwip/src/core/tcp_out.c55
-rw-r--r--Libraries/Lwip/lwip/src/core/udp.c145
-rw-r--r--Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxyfile.doxygen1645
-rw-r--r--Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxygen_module_mainpage.h58
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv4/lwip/autoip.h3
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv4/lwip/ip.h42
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv4/lwip/ip_addr.h2
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv6/lwip/icmp.h100
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv6/lwip/inet.h68
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv6/lwip/ip.h130
-rw-r--r--Libraries/Lwip/lwip/src/include/ipv6/lwip/ip_addr.h97
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/api.h25
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/api_msg.h7
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/arch.h23
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/debug.h3
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/def.h7
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/init.h2
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/mem.h1
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/memp_std.h4
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/netif.h13
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/opt.h214
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/pbuf.h28
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/sockets.h9
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/stats.h38
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/sys.h43
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/tcp.h34
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/tcp_impl.h41
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/tcpip.h10
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/timers.h4
-rw-r--r--Libraries/Lwip/lwip/src/include/lwip/udp.h2
-rw-r--r--Libraries/Lwip/lwip/src/include/netif/etharp.h1
-rw-r--r--Libraries/Lwip/lwip/src/include/netif/slipif.h81
-rw-r--r--Libraries/Lwip/lwip/src/netif/etharp.c264
-rw-r--r--Libraries/Lwip/lwip/src/netif/ethernetif.c317
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/auth.c1334
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/auth.h111
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/chap.c908
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/chap.h150
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/chpms.c396
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/chpms.h64
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/fsm.c890
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/fsm.h157
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/ipcp.c1411
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/ipcp.h106
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/lcp.c2066
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/lcp.h151
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/magic.c80
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/magic.h63
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/md5.c320
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/md5.h55
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/pap.c628
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/pap.h118
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/ppp.c2045
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/ppp.h201
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/ppp_impl.h363
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/ppp_oe.c1132
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/pppdebug.h73
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/randm.c249
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/randm.h81
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/vj.c652
-rw-r--r--Libraries/Lwip/lwip/src/netif/ppp/vj.h156
-rw-r--r--Libraries/Lwip/lwip/src/netif/slipif.c510
-rw-r--r--Libraries/Lwip/lwip/test/unit/core/test_mem.c73
-rw-r--r--Libraries/Lwip/lwip/test/unit/core/test_mem.h8
-rw-r--r--Libraries/Lwip/lwip/test/unit/etharp/test_etharp.c262
-rw-r--r--Libraries/Lwip/lwip/test/unit/etharp/test_etharp.h8
-rw-r--r--Libraries/Lwip/lwip/test/unit/lwip_check.h37
-rw-r--r--Libraries/Lwip/lwip/test/unit/lwip_unittests.c45
-rw-r--r--Libraries/Lwip/lwip/test/unit/lwipopts.h50
-rw-r--r--Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.c294
-rw-r--r--Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.h52
-rw-r--r--Libraries/Lwip/lwip/test/unit/tcp/test_tcp.c667
-rw-r--r--Libraries/Lwip/lwip/test/unit/tcp/test_tcp.h8
-rw-r--r--Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.c944
-rw-r--r--Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.h8
-rw-r--r--Libraries/Lwip/lwip/test/unit/udp/test_udp.c68
-rw-r--r--Libraries/Lwip/lwip/test/unit/udp/test_udp.h8
-rw-r--r--Libraries/Lwip/lwipopts.h266
-rw-r--r--Release/RepRapFirmware-1.09x-beta3.binbin0 -> 271452 bytes
-rw-r--r--SD-image/sys-CoreXY/iap.binbin0 -> 30280 bytes
-rw-r--r--SD-image/sys-Fisher/iap.binbin0 -> 30280 bytes
-rw-r--r--SD-image/sys-Huxley/iap.binbin0 -> 30280 bytes
-rw-r--r--SD-image/sys-MiniKossel/iap.binbin0 -> 30280 bytes
-rw-r--r--SD-image/sys-Ormerod1/iap.binbin0 -> 30280 bytes
-rw-r--r--SD-image/sys-Ormerod2/iap.binbin0 -> 30280 bytes
-rw-r--r--SD-image/www/.gitattributes22
-rw-r--r--SD-image/www/css/defaults.css1
-rw-r--r--SD-image/www/js/jquery.autosize.js274
-rw-r--r--SD-image/www/js/jquery.autosize.min.js6
-rw-r--r--SD-image/www/js/jquery.textarea_autosize.js54
-rw-r--r--SD-image/www/js/jquery.textarea_autosize.min.js1
-rw-r--r--SD-image/www/js/model.js197
-rw-r--r--SD-image/www/js/viewmodel.js17
-rw-r--r--SD-image/www/language.xml23
-rw-r--r--SD-image/www/reprap.htm255
-rw-r--r--src/Configuration.h8
-rw-r--r--src/DDA.cpp23
-rw-r--r--src/DDA.h10
-rw-r--r--src/GCodeBuffer.cpp53
-rw-r--r--src/GCodeBuffer.h19
-rw-r--r--src/GCodes.cpp33
-rw-r--r--src/GCodes.h3
-rw-r--r--src/MassStorage.cpp8
-rw-r--r--src/MassStorage.h35
-rw-r--r--src/Move.cpp9
-rw-r--r--src/Move.h2
-rw-r--r--src/Network.cpp527
-rw-r--r--src/Network.h33
-rw-r--r--src/Pins_duet.h4
-rw-r--r--src/Platform.cpp122
-rw-r--r--src/Platform.h9
-rw-r--r--src/PrintMonitor.cpp412
-rw-r--r--src/PrintMonitor.h21
-rw-r--r--src/Reprap.cpp139
-rw-r--r--src/Reprap.h8
-rw-r--r--src/Webserver.cpp374
-rw-r--r--src/Webserver.h57
169 files changed, 34838 insertions, 2999 deletions
diff --git a/.cproject b/.cproject
index de639ff0..fec21327 100644
--- a/.cproject
+++ b/.cproject
@@ -40,6 +40,7 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Libraries/SD_HSMCI/utility}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Libraries/SPI}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Libraries/Lwip}&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Libraries/Lwip/lwip/src/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Libraries/EMAC}&quot;"/>
</option>
<option id="gnu.c.compiler.option.preprocessor.def.symbols.2036576066" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols">
@@ -106,7 +107,7 @@
</toolChain>
</folderInfo>
<sourceEntries>
- <entry excluding="DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/as_gcc|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/gcc_atmel|DuetArduinoCore/system/CMSIS/Device/ARM|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/gcc_arm|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/iar|DuetArduinoCore/system/CMSIS/CMSIS/Lib/ARM|DuetArduinoCore/system/CMSIS/CMSIS/DSP_Lib|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam4.h|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam4s|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3u|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3s|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3n|DuetArduinoCore/system/CMSIS/Device/ARM/ARMCM4|DuetArduinoCore/system/CMSIS/Device/ARM/ARMCM0|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3sd8" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
+ <entry excluding="Libraries/Lwip/lwip/src/core/ipv6|Libraries/Lwip/lwip/test|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/as_gcc|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/gcc_atmel|DuetArduinoCore/system/CMSIS/Device/ARM|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/gcc_arm|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3xa/source/iar|DuetArduinoCore/system/CMSIS/CMSIS/Lib/ARM|DuetArduinoCore/system/CMSIS/CMSIS/DSP_Lib|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam4.h|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam4s|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3u|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3s|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3n|DuetArduinoCore/system/CMSIS/Device/ARM/ARMCM4|DuetArduinoCore/system/CMSIS/Device/ARM/ARMCM0|DuetArduinoCore/system/CMSIS/Device/ATMEL/sam3sd8" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>
@@ -125,11 +126,11 @@
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
- <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.release.516195201;cdt.managedbuild.config.gnu.cross.exe.release.516195201.;cdt.managedbuild.tool.gnu.cross.c.compiler.220085372;cdt.managedbuild.tool.gnu.c.compiler.input.1345445195">
- <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
- </scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.release.516195201;cdt.managedbuild.config.gnu.cross.exe.release.516195201.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1703390221;cdt.managedbuild.tool.gnu.cpp.compiler.input.111814721">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
</scannerConfigBuildInfo>
+ <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.release.516195201;cdt.managedbuild.config.gnu.cross.exe.release.516195201.;cdt.managedbuild.tool.gnu.cross.c.compiler.220085372;cdt.managedbuild.tool.gnu.c.compiler.input.1345445195">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+ </scannerConfigBuildInfo>
</storageModule>
</cproject>
diff --git a/.settings/org.eclipse.cdt.codan.core.prefs b/.settings/org.eclipse.cdt.codan.core.prefs
index 77386c23..a7d4a2ef 100644
--- a/.settings/org.eclipse.cdt.codan.core.prefs
+++ b/.settings/org.eclipse.cdt.codan.core.prefs
@@ -19,7 +19,7 @@ org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
-org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=-Warning
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true}
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
@@ -37,7 +37,7 @@ org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
-org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
@@ -65,3 +65,4 @@ org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")}
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+useParentScope=false
diff --git a/Libraries/Lwip/lwip/src/sam/include/arch/cc.h b/Libraries/EMAC/arch/cc.h
index 3467a0a9..7e00c367 100644
--- a/Libraries/Lwip/lwip/src/sam/include/arch/cc.h
+++ b/Libraries/EMAC/arch/cc.h
@@ -3,7 +3,7 @@
*
* \brief lwIP abstraction layer for SAM.
*
- * Copyright (c) 2012 Atmel Corporation. All rights reserved.
+ * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
@@ -44,31 +44,28 @@
#ifndef CC_H_INCLUDED
#define CC_H_INCLUDED
-//#include "stdio_serial.h"
+#include <stdio.h>
+#include <stdint.h>
/* Define platform endianness */
#define BYTE_ORDER LITTLE_ENDIAN
-/* The unsigned data types */
-typedef unsigned char u8_t;
-typedef unsigned short u16_t;
-typedef unsigned int u32_t;
+/* Types based on stdint.h */
+typedef uint8_t u8_t;
+typedef int8_t s8_t;
+typedef uint16_t u16_t;
+typedef int16_t s16_t;
+typedef uint32_t u32_t;
+typedef int32_t s32_t;
+typedef uintptr_t mem_ptr_t;
-/* The signed counterparts */
-typedef signed char s8_t;
-typedef signed short s16_t;
-typedef signed int s32_t;
-
-/* A generic pointer type */
-typedef u32_t mem_ptr_t;
-
-/* Display name of types */
-#define U16_F "hu"
-#define S16_F "hd"
-#define X16_F "hx"
-#define U32_F "u"
-#define S32_F "d"
-#define X32_F "x"
+/* Define (sn)printf formatters for these lwIP types */
+#define U16_F "hu"
+#define S16_F "hd"
+#define X16_F "hx"
+#define U32_F "u"
+#define S32_F "d"
+#define X32_F "x"
/* Compiler hints for packing lwip's structures */
#if defined(__CC_ARM)
@@ -99,15 +96,12 @@ typedef u32_t mem_ptr_t;
*/
#define LWIP_COMPAT_MUTEX 1
-/* Plaform specific diagnostic output */
-void debugPrintf(const char*, ...);
-
-#define LWIP_PLATFORM_DIAG(x) do {debugPrintf x;} while (0)
-
-#define LWIP_PLATFORM_ASSERT(x) do { debugPrintf("Assertion \"%s\" failed at line %d in %s\n", x, __LINE__, __FILE__); \
- fflush(NULL); abort(); }while(0)
-
+/* Make lwip/arch.h define the codes which are used throughout */
+#define LWIP_PROVIDE_ERRNO
-#define LWIP_PROVIDE_ERRNO /* Make lwip/arch.h define the codes which are used throughout */
+/* Debug facilities. LWIP_DEBUG must be defined to read output */
+extern void debugPrintf(const char *, ...);
+#define LWIP_PLATFORM_DIAG(x) {debugPrintf x;}
+#define LWIP_PLATFORM_ASSERT(x) {debugPrintf("Assertion \"%s\" failed at line %d in %s\n", x, __LINE__, __FILE__); while(1);}
#endif /* CC_H_INCLUDED */
diff --git a/Libraries/Lwip/lwip/src/sam/include/arch/perf.h b/Libraries/EMAC/arch/perf.h
index 6387b70b..936ea4af 100644
--- a/Libraries/Lwip/lwip/src/sam/include/arch/perf.h
+++ b/Libraries/EMAC/arch/perf.h
@@ -3,7 +3,7 @@
*
* \brief lwIP abstraction layer for SAM.
*
- * Copyright (c) 2012 Atmel Corporation. All rights reserved.
+ * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
@@ -44,7 +44,7 @@
#ifndef PERF_H_INCLUDED
#define PERF_H_INCLUDED
-#define PERF_START /* NULL definition */
-#define PERF_STOP(_arg) /* NULL definition */
+#define PERF_START /* null definition */
+#define PERF_STOP(x) /* null definition */
#endif /* PERF_H_INCLUDED */
diff --git a/Libraries/EMAC/emac.h b/Libraries/EMAC/emac.h
new file mode 100644
index 00000000..16c6d6e0
--- /dev/null
+++ b/Libraries/EMAC/emac.h
@@ -0,0 +1,1226 @@
+ /**
+ * \file
+ *
+ * \brief EMAC (Ethernet MAC) driver for SAM.
+ *
+ * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * 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 Atmel may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 4. This software may only be redistributed and used in connection with an
+ * Atmel microcontroller product.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
+ *
+ * \asf_license_stop
+ *
+ */
+
+#ifndef EMAC_H_INCLUDED
+#define EMAC_H_INCLUDED
+
+#include "../chip.h"
+//#include "conf_eth.h"
+
+/// @cond 0
+/**INDENT-OFF**/
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**INDENT-ON**/
+/// @endcond
+
+/** The buffer addresses written into the descriptors must be aligned, so the
+ last few bits are zero. These bits have special meaning for the EMAC
+ peripheral and cannot be used as part of the address. */
+#define EMAC_RXD_ADDR_MASK 0xFFFFFFFC
+#define EMAC_RXD_WRAP (1ul << 1) /**< Wrap bit */
+#define EMAC_RXD_OWNERSHIP (1ul << 0) /**< Ownership bit */
+
+#define EMAC_RXD_BROADCAST (1ul << 31) /**< Broadcast detected */
+#define EMAC_RXD_MULTIHASH (1ul << 30) /**< Multicast hash match */
+#define EMAC_RXD_UNIHASH (1ul << 29) /**< Unicast hash match */
+#define EMAC_RXD_EXTADDR (1ul << 28) /**< External address match */
+#define EMAC_RXD_ADDR1 (1ul << 26) /**< Address 1 match */
+#define EMAC_RXD_ADDR2 (1ul << 25) /**< Address 2 match */
+#define EMAC_RXD_ADDR3 (1ul << 24) /**< Address 3 match */
+#define EMAC_RXD_ADDR4 (1ul << 23) /**< Address 4 match */
+#define EMAC_RXD_TYPE (1ul << 22) /**< Type ID match */
+#define EMAC_RXD_VLAN (1ul << 21) /**< VLAN tag detected */
+#define EMAC_RXD_PRIORITY (1ul << 20) /**< Priority tag detected */
+#define EMAC_RXD_PRIORITY_MASK (3ul << 17) /**< VLAN priority */
+#define EMAC_RXD_CFI (1ul << 16) /**< Concatenation Format Indicator only if bit 21 is set */
+#define EMAC_RXD_EOF (1ul << 15) /**< End of frame */
+#define EMAC_RXD_SOF (1ul << 14) /**< Start of frame */
+#define EMAC_RXD_OFFSET_MASK /**< Receive buffer offset */
+#define EMAC_RXD_LEN_MASK (0xFFF) /**< Length of frame including FCS (if selected) */
+#define EMAC_RXD_LENJUMBO_MASK (0x3FFF) /**< Jumbo frame length */
+
+#define EMAC_TXD_USED (1ul << 31) /**< Frame is transmitted */
+#define EMAC_TXD_WRAP (1ul << 30) /**< Last descriptor */
+#define EMAC_TXD_ERROR (1ul << 29) /**< Retry limit exceeded, error */
+#define EMAC_TXD_UNDERRUN (1ul << 28) /**< Transmit underrun */
+#define EMAC_TXD_EXHAUSTED (1ul << 27) /**< Buffer exhausted */
+#define EMAC_TXD_NOCRC (1ul << 16) /**< No CRC */
+#define EMAC_TXD_LAST (1ul << 15) /**< Last buffer in frame */
+#define EMAC_TXD_LEN_MASK (0x7FF) /**< Length of buffer */
+
+/** The MAC can support frame lengths up to 1536 bytes */
+#define EMAC_FRAME_LENTGH_MAX 1536
+
+#define EMAC_RX_UNITSIZE 128 /**< Fixed size for RX buffer */
+#define EMAC_TX_UNITSIZE 1518 /**< Size for ETH frame length */
+
+/** EMAC clock speed */
+#define EMAC_CLOCK_SPEED_160MHZ (160*1000*1000)
+#define EMAC_CLOCK_SPEED_80MHZ (80*1000*1000)
+#define EMAC_CLOCK_SPEED_40MHZ (40*1000*1000)
+#define EMAC_CLOCK_SPEED_20MHZ (20*1000*1000)
+
+/** EMAC maintain code default value*/
+#define EMAC_MAN_CODE_VALUE (10)
+
+/** EMAC maintain start of frame default value*/
+#define EMAC_MAN_SOF_VALUE (1)
+
+/** EMAC maintain read/write*/
+#define EMAC_MAN_RW_TYPE (2)
+
+/** EMAC maintain read only*/
+#define EMAC_MAN_READ_ONLY (1)
+
+/** EMAC address length */
+#define EMAC_ADDR_LENGTH (6)
+
+/**
+ * \brief Return codes for EMAC APIs.
+ */
+typedef enum {
+ EMAC_OK = 0, /** Operation OK */
+ EMAC_TIMEOUT = 1, /** EMAC operation timeout */
+ EMAC_TX_BUSY, /** TX in progress */
+ EMAC_RX_NULL, /** No data received */
+ EMAC_SIZE_TOO_SMALL, /** Buffer size not enough */
+ EMAC_PARAM, /** Parameter error, TX packet invalid or RX size too small */
+ EMAC_INVALID = 0xFF, /* Invalid */
+} emac_status_t;
+
+#if defined __ICCARM__
+#pragma pack(4) /* IAR */
+#define __attribute__(...) /* IAR */
+#endif /* IAR */
+
+/** Receive buffer descriptor struct */
+typedef struct emac_rx_descriptor {
+ union emac_rx_addr {
+ uint32_t val;
+ struct emac_rx_addr_bm {
+ uint32_t b_ownership:1, /**< User clear, EMAC sets this to 1 once it has successfully written a frame to memory */
+ b_wrap:1, /**< Marks last descriptor in receive buffer */
+ addr_dw:30; /**< Address in number of DW */
+ } bm;
+ } addr; /**< Address, Wrap & Ownership */
+ union emac_rx_status {
+ uint32_t val;
+ struct emac_rx_status_bm {
+ uint32_t len:12, /** Length of frame including FCS */
+ offset:2, /** Receive buffer offset, bits 13:12 of frame length for jumbo frame */
+ b_sof:1, /** Start of frame */
+ b_eof:1, /** End of frame */
+ b_cfi:1, /** Concatenation Format Indicator */
+ vlan_priority:3, /** VLAN priority (if VLAN detected) */
+ b_priority_detected:1, /** Priority tag detected */
+ b_vlan_detected:1, /**< VLAN tag detected */
+ b_type_id_match:1, /**< Type ID match */
+ b_addr4match:1, /**< Address register 4 match */
+ b_addr3match:1, /**< Address register 3 match */
+ b_addr2match:1, /**< Address register 2 match */
+ b_addr1match:1, /**< Address register 1 match */
+ reserved:1,
+ b_ext_addr_match:1, /**< External address match */
+ b_uni_hash_match:1, /**< Unicast hash match */
+ b_multi_hash_match:1, /**< Multicast hash match */
+ b_boardcast_detect:1; /**< Global broadcast address detected */
+ } bm;
+ } status;
+} __attribute__ ((packed, aligned(8))) emac_rx_descriptor_t; /* GCC */
+
+/** Transmit buffer descriptor struct */
+typedef struct emac_tx_descriptor {
+ uint32_t addr;
+ union emac_tx_status {
+ uint32_t val;
+ struct emac_tx_status_bm {
+ uint32_t len:11, /**< Length of buffer */
+ reserved:4,
+ b_last_buffer:1, /**< Last buffer (in the current frame) */
+ b_no_crc:1, /**< No CRC */
+ reserved1:10,
+ b_exhausted:1, /**< Buffer exhausted in mid frame */
+ b_underrun:1, /**< Transmit underrun */
+ b_error:1, /**< Retry limit exceeded, error detected */
+ b_wrap:1, /**< Marks last descriptor in TD list */
+ b_used:1; /**< User clear, EMAC sets this to 1 once a frame has been successfully transmitted */
+ } bm;
+ } status;
+} __attribute__ ((packed, aligned(8))) emac_tx_descriptor_t; /* GCC */
+
+#ifdef __ICCARM__ /* IAR */
+#pragma pack()
+#endif
+
+/**
+ * \brief Input parameters when initializing the emac module mode.
+ */
+typedef struct emac_options {
+ /* Enable/Disable CopyAllFrame */
+ uint8_t uc_copy_all_frame;
+ /* Enable/Disable NoBroadCast */
+ uint8_t uc_no_boardcast;
+ /* MAC address */
+ uint8_t uc_mac_addr[EMAC_ADDR_LENGTH];
+} emac_options_t;
+
+/** RX callback */
+typedef void (*emac_dev_tx_cb_t) (uint32_t ul_status);
+/** Wakeup callback */
+typedef void (*emac_dev_wakeup_cb_t) (void);
+
+/**
+ * EMAC driver structure.
+ */
+typedef struct emac_device {
+
+ /** Pointer to HW register base */
+ Emac *p_hw;
+ /**
+ * Pointer to allocated TX buffer.
+ * Section 3.6 of AMBA 2.0 spec states that burst should not cross
+ * 1K Boundaries.
+ * Receive buffer manager writes are burst of 2 words => 3 lsb bits
+ * of the address shall be set to 0.
+ */
+ uint8_t *p_tx_buffer;
+ /** Pointer to allocated RX buffer */
+ uint8_t *p_rx_buffer;
+ /** Pointer to Rx TDs (must be 8-byte aligned) */
+ emac_rx_descriptor_t *p_rx_dscr;
+ /** Pointer to Tx TDs (must be 8-byte aligned) */
+ emac_tx_descriptor_t *p_tx_dscr;
+ /** Optional callback to be invoked once a frame has been received */
+ emac_dev_tx_cb_t func_rx_cb;
+ /** Optional callback to be invoked once several TDs have been released */
+ emac_dev_wakeup_cb_t func_wakeup_cb;
+ /** Optional callback list to be invoked once TD has been processed */
+ emac_dev_tx_cb_t *func_tx_cb_list;
+ /** RX TD list size */
+ uint16_t us_rx_list_size;
+ /** RX index for current processing TD */
+ uint16_t us_rx_idx;
+ /** TX TD list size */
+ uint16_t us_tx_list_size;
+ /** Circular buffer head pointer by upper layer (buffer to be sent) */
+ uint16_t us_tx_head;
+ /** Circular buffer tail pointer incremented by handlers (buffer sent) */
+ uint16_t us_tx_tail;
+
+ /** Number of free TD before wakeup callback is invoked */
+ uint8_t uc_wakeup_threshold;
+} emac_device_t;
+
+/**
+ * \brief Write network control value.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_ncr Network control value.
+ */
+static inline void emac_network_control(Emac* p_emac, uint32_t ul_ncr)
+{
+ p_emac->EMAC_NCR = ul_ncr;
+}
+
+/**
+ * \brief Get network control value.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+
+static inline uint32_t emac_get_network_control(Emac* p_emac)
+{
+ return p_emac->EMAC_NCR;
+}
+
+/**
+ * \brief Enable/Disable EMAC receive.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable EMAC receiver, else to enable it.
+ */
+static inline void emac_enable_receive(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCR |= EMAC_NCR_RE;
+ } else {
+ p_emac->EMAC_NCR &= ~EMAC_NCR_RE;
+ }
+}
+
+/**
+ * \brief Enable/Disable EMAC transmit.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable EMAC transmit, else to enable it.
+ */
+static inline void emac_enable_transmit(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCR |= EMAC_NCR_TE;
+ } else {
+ p_emac->EMAC_NCR &= ~EMAC_NCR_TE;
+ }
+}
+
+/**
+ * \brief Enable/Disable EMAC management.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable EMAC management, else to enable it.
+ */
+static inline void emac_enable_management(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCR |= EMAC_NCR_MPE;
+ } else {
+ p_emac->EMAC_NCR &= ~EMAC_NCR_MPE;
+ }
+}
+
+/**
+ * \brief Clear all statistics registers.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+static inline void emac_clear_statistics(Emac* p_emac)
+{
+ p_emac->EMAC_NCR |= EMAC_NCR_CLRSTAT;
+}
+
+/**
+ * \brief Increase all statistics registers.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+static inline void emac_increase_statistics(Emac* p_emac)
+{
+ p_emac->EMAC_NCR |= EMAC_NCR_INCSTAT;
+}
+
+/**
+ * \brief Enable/Disable statistics registers writing.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the statistics registers writing, else to enable it.
+ */
+static inline void emac_enable_statistics_write(Emac* p_emac,
+ uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCR |= EMAC_NCR_WESTAT;
+ } else {
+ p_emac->EMAC_NCR &= ~EMAC_NCR_WESTAT;
+ }
+}
+
+/**
+ * \brief In half-duplex mode, forces collisions on all received frames.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the back pressure, else to enable it.
+ */
+static inline void emac_enable_back_pressure(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCR |= EMAC_NCR_BP;
+ } else {
+ p_emac->EMAC_NCR &= ~EMAC_NCR_BP;
+ }
+}
+
+/**
+ * \brief Start transmission.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+static inline void emac_start_transmission(Emac* p_emac)
+{
+ p_emac->EMAC_NCR |= EMAC_NCR_TSTART;
+}
+
+/**
+ * \brief Halt transmission.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+static inline void emac_halt_transmission(Emac* p_emac)
+{
+ p_emac->EMAC_NCR |= EMAC_NCR_THALT;
+}
+
+/**
+ * \brief Set up network configuration register.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_cfg Network configuration value.
+ */
+static inline void emac_set_configure(Emac* p_emac, uint32_t ul_cfg)
+{
+ p_emac->EMAC_NCFGR = ul_cfg;
+}
+
+/**
+ * \brief Get network configuration.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Network configuration.
+ */
+static inline uint32_t emac_get_configure(Emac* p_emac)
+{
+ return p_emac->EMAC_NCFGR;
+}
+
+/**
+ * \brief Set speed.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_speed 1 to indicate 100Mbps, 0 to 10Mbps.
+ */
+static inline void emac_set_speed(Emac* p_emac, uint8_t uc_speed)
+{
+ if (uc_speed) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_SPD;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_SPD;
+ }
+}
+
+/**
+ * \brief Enable/Disable Full-Duplex mode.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the Full-Duplex mode, else to enable it.
+ */
+static inline void emac_enable_full_duplex(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_FD;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_FD;
+ }
+}
+
+/**
+ * \brief Enable/Disable Copy(Receive) All Valid Frames.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable copying all valid frames, else to enable it.
+ */
+static inline void emac_enable_copy_all(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_CAF;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_CAF;
+ }
+}
+
+/**
+ * \brief Enable/Disable jumbo frames (up to 10240 bytes).
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the jumbo frames, else to enable it.
+ */
+static inline void emac_enable_jumbo_frames(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_JFRAME;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_JFRAME;
+ }
+}
+
+/**
+ * \brief Disable/Enable broadcast receiving.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 1 to disable the broadcast, else to enable it.
+ */
+static inline void emac_disable_broadcast(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_NBC;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_NBC;
+ }
+}
+
+/**
+ * \brief Enable/Disable multicast hash.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the multicast hash, else to enable it.
+ */
+static inline void emac_enable_multicast_hash(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_UNI;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_UNI;
+ }
+}
+
+/**
+ * \brief Enable/Disable big frames (over 1518, up to 1536).
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable big frames else to enable it.
+ */
+static inline void emac_enable_big_frame(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_BIG;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_BIG;
+ }
+}
+
+/**
+ * \brief Set MDC clock divider.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_mck EMAC MCK.
+ *
+ * \return EMAC_OK if successfully.
+ */
+static inline uint8_t emac_set_clock(Emac* p_emac, uint32_t ul_mck)
+{
+ uint32_t ul_clk;
+
+ if (ul_mck > EMAC_CLOCK_SPEED_160MHZ) {
+ return EMAC_INVALID;
+ } else if (ul_mck > EMAC_CLOCK_SPEED_80MHZ) {
+ ul_clk = EMAC_NCFGR_CLK_MCK_64;
+ } else if (ul_mck > EMAC_CLOCK_SPEED_40MHZ) {
+ ul_clk = EMAC_NCFGR_CLK_MCK_32;
+ } else if (ul_mck > EMAC_CLOCK_SPEED_20MHZ) {
+ ul_clk = EMAC_NCFGR_CLK_MCK_16;
+ } else {
+ ul_clk = EMAC_NCFGR_CLK_MCK_8;
+ }
+
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_CLK_Msk;
+ p_emac->EMAC_NCFGR |= ul_clk;
+
+ return EMAC_OK;
+}
+
+/**
+ * \brief Enable/Disable retry test.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the EMAC receiver, else to enable it.
+ */
+static inline void emac_enable_retry_test(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_RTY;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_RTY;
+ }
+}
+
+/**
+ * \brief Enable/Disable pause (when a valid pause frame is received).
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable pause frame, else to enable it.
+ */
+static inline void emac_enable_pause_frame(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_PAE;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_PAE;
+ }
+}
+
+/**
+ * \brief Set receive buffer offset to 0 ~ 3.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+static inline void emac_set_rx_buffer_offset(Emac* p_emac, uint8_t uc_offset)
+{
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_RBOF_Msk;
+ p_emac->EMAC_NCFGR |=
+ (EMAC_NCFGR_RBOF_Msk & ((uc_offset) << EMAC_NCFGR_RBOF_Pos));
+}
+
+/**
+ * \brief Enable/Disable receive length field checking.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable receive length field checking, else to enable it.
+ */
+static inline void emac_enable_rx_length_check(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_RLCE;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_RLCE;
+ }
+}
+
+/**
+ * \brief Enable/Disable discarding FCS field of received frames.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable discarding FCS field of received frames, else to enable it.
+ */
+static inline void emac_enable_discard_fcs(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_DRFCS;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_DRFCS;
+ }
+}
+
+
+/**
+ * \brief Enable/Disable frames to be received in half-duplex mode
+ * while transmitting.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the received in half-duplex mode, else to enable it.
+ */
+static inline void emac_enable_efrhd(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_EFRHD;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_EFRHD;
+ }
+}
+
+/**
+ * \brief Enable/Disable ignore RX FCS.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable ignore RX FCS, else to enable it.
+ */
+static inline void emac_enable_ignore_rx_fcs(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_NCFGR |= EMAC_NCFGR_IRXFCS;
+ } else {
+ p_emac->EMAC_NCFGR &= ~EMAC_NCFGR_IRXFCS;
+ }
+}
+
+/**
+ * \brief Get Network Status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Network status.
+ */
+static inline uint32_t emac_get_status(Emac* p_emac)
+{
+ return p_emac->EMAC_NSR;
+}
+
+/**
+ * \brief Get MDIO IN pin status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return MDIO IN pin status.
+ */
+static inline uint8_t emac_get_MDIO(Emac* p_emac)
+{
+ return ((p_emac->EMAC_NSR & EMAC_NSR_MDIO) > 0);
+}
+
+/**
+ * \brief Check if PHY is idle.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return 1 if PHY is idle.
+ */
+static inline uint8_t emac_is_phy_idle(Emac* p_emac)
+{
+ return ((p_emac->EMAC_NSR & EMAC_NSR_IDLE) > 0);
+}
+
+/**
+ * \brief Return transmit status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Transmit status.
+ */
+static inline uint32_t emac_get_tx_status(Emac* p_emac)
+{
+ return p_emac->EMAC_TSR;
+}
+
+/**
+ * \brief Clear transmit status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_status Transmit status.
+ */
+static inline void emac_clear_tx_status(Emac* p_emac, uint32_t ul_status)
+{
+ p_emac->EMAC_TSR = ul_status;
+}
+
+/**
+ * \brief Return receive status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ */
+static inline uint32_t emac_get_rx_status(Emac* p_emac)
+{
+ return p_emac->EMAC_RSR;
+}
+
+/**
+ * \brief Clear receive status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_status Receive status.
+ */
+static inline void emac_clear_rx_status(Emac* p_emac, uint32_t ul_status)
+{
+ p_emac->EMAC_RSR = ul_status;
+}
+
+/**
+ * \brief Set Rx Queue.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_addr Rx queue address.
+ */
+static inline void emac_set_rx_queue(Emac* p_emac, uint32_t ul_addr)
+{
+ p_emac->EMAC_RBQP = EMAC_RBQP_ADDR_Msk & ul_addr;
+}
+
+/**
+ * \brief Get Rx Queue Address.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Rx queue address.
+ */
+static inline uint32_t emac_get_rx_queue(Emac* p_emac)
+{
+ return p_emac->EMAC_RBQP;
+}
+
+/**
+ * \brief Set Tx Queue.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_addr Tx queue address.
+ */
+static inline void emac_set_tx_queue(Emac* p_emac, uint32_t ul_addr)
+{
+ p_emac->EMAC_TBQP = EMAC_TBQP_ADDR_Msk & ul_addr;
+}
+
+/**
+ * \brief Get Tx Queue.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Rx queue address.
+ */
+static inline uint32_t emac_get_tx_queue(Emac* p_emac)
+{
+ return p_emac->EMAC_TBQP;
+}
+
+/**
+ * \brief Enable interrupt(s).
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_source Interrupt source(s) to be enabled.
+ */
+static inline void emac_enable_interrupt(Emac* p_emac, uint32_t ul_source)
+{
+ p_emac->EMAC_IER = ul_source;
+}
+
+/**
+ * \brief Disable interrupt(s).
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_source Interrupt source(s) to be disabled.
+ */
+static inline void emac_disable_interrupt(Emac* p_emac, uint32_t ul_source)
+{
+ p_emac->EMAC_IDR = ul_source;
+}
+
+/**
+ * \brief Return interrupt status.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Interrupt status.
+ */
+static inline uint32_t emac_get_interrupt_status(Emac* p_emac)
+{
+ return p_emac->EMAC_ISR;
+}
+
+/**
+ * \brief Return interrupt mask.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Interrupt mask.
+ */
+static inline uint32_t emac_get_interrupt_mask(Emac* p_emac)
+{
+ return p_emac->EMAC_IMR;
+}
+
+/**
+ * \brief Execute PHY maintenance command.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_phy_addr PHY address.
+ * \param uc_reg_addr Register address.
+ * \param uc_rw 1 to Read, 0 to write.
+ * \param us_data Data to be performed, write only.
+ */
+static inline void emac_maintain_phy(Emac* p_emac,
+ uint8_t uc_phy_addr, uint8_t uc_reg_addr, uint8_t uc_rw,
+ uint16_t us_data)
+{
+ /* Wait until bus idle */
+ while ((p_emac->EMAC_NSR & EMAC_NSR_IDLE) == 0);
+ /* Write maintain register */
+ p_emac->EMAC_MAN = EMAC_MAN_CODE(EMAC_MAN_CODE_VALUE)
+ | EMAC_MAN_SOF(EMAC_MAN_SOF_VALUE)
+ | EMAC_MAN_PHYA(uc_phy_addr)
+ | EMAC_MAN_REGA(uc_reg_addr)
+ | EMAC_MAN_RW((uc_rw ? EMAC_MAN_RW_TYPE : EMAC_MAN_READ_ONLY))
+ | EMAC_MAN_DATA(us_data);
+}
+
+/**
+ * \brief Get PHY maintenance data returned.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Get PHY data.
+ */
+static inline uint16_t emac_get_phy_data(Emac* p_emac)
+{
+ /* Wait until bus idle */
+ while ((p_emac->EMAC_NSR & EMAC_NSR_IDLE) == 0);
+ /* Return data */
+ return (uint16_t) (p_emac->EMAC_MAN & EMAC_MAN_DATA_Msk);
+}
+
+/**
+ * \brief Set pause time.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param us_pause_time Pause time.
+ */
+static inline void emac_set_pause_time(Emac* p_emac, uint16_t us_pause_time)
+{
+ p_emac->EMAC_PTR = us_pause_time;
+}
+
+/**
+ * \brief Set Hash.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ul_hash_top Hash top.
+ * \param ul_hash_bottom Hash bottom.
+ */
+static inline void emac_set_hash(Emac* p_emac, uint32_t ul_hash_top,
+ uint32_t ul_hash_bottom)
+{
+ p_emac->EMAC_HRB = ul_hash_bottom;
+ p_emac->EMAC_HRT = ul_hash_top;
+}
+
+/**
+ * \brief Set 64 bits Hash.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param ull_hash 64 bits hash value.
+ */
+static inline void emac_set_hash64(Emac* p_emac, uint64_t ull_hash)
+{
+ p_emac->EMAC_HRB = (uint32_t) ull_hash;
+ p_emac->EMAC_HRT = (uint32_t) (ull_hash >> 32);
+}
+
+/**
+ * \brief Set MAC Address.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_index EMAC specific address register index.
+ * \param p_mac_addr EMAC address.
+ */
+static inline void emac_set_address(Emac* p_emac, uint8_t uc_index,
+ uint8_t* p_mac_addr)
+{
+ p_emac->EMAC_SA[uc_index].EMAC_SAxB = (p_mac_addr[3] << 24)
+ | (p_mac_addr[2] << 16)
+ | (p_mac_addr[1] << 8)
+ | (p_mac_addr[0]);
+ p_emac->EMAC_SA[uc_index].EMAC_SAxT = (p_mac_addr[5] << 8)
+ | (p_mac_addr[4]);
+}
+
+/**
+ * \brief Set MAC Address via 2 dword.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_index EMAC specific address register index.
+ * \param ul_mac_top EMAC top address.
+ * \param ul_mac_bottom EMAC bottom address.
+ */
+static inline void emac_set_address32(Emac* p_emac, uint8_t uc_index,
+ uint32_t ul_mac_top, uint32_t ul_mac_bottom)
+{
+ p_emac->EMAC_SA[uc_index].EMAC_SAxB = ul_mac_bottom;
+ p_emac->EMAC_SA[uc_index].EMAC_SAxT = ul_mac_top;
+}
+
+/**
+ * \brief Set MAC Address via int64.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_index EMAC specific address register index.
+ * \param ull_mac 64-bit EMAC address.
+ */
+static inline void emac_set_address64(Emac* p_emac, uint8_t uc_index,
+ uint64_t ull_mac)
+{
+ p_emac->EMAC_SA[uc_index].EMAC_SAxB = (uint32_t) ull_mac;
+ p_emac->EMAC_SA[uc_index].EMAC_SAxT = (uint32_t) (ull_mac >> 32);
+}
+
+/**
+ * \brief Set type ID.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param us_type_id Type to be set.
+ */
+static inline void emac_set_type_id(Emac* p_emac, uint16_t us_type_id)
+{
+ p_emac->EMAC_TID = EMAC_TID_TID(us_type_id);
+}
+
+/**
+ * \brief Get type ID.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ *
+ * \return Type ID.
+ */
+static inline uint16_t emac_get_type_id(Emac* p_emac)
+{
+ return (p_emac->EMAC_TID & EMAC_TID_TID_Msk);
+}
+
+/**
+ * \brief Enable/Disable RMII.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable the RMII mode, else to enable it.
+ */
+static inline void emac_enable_rmii(Emac* p_emac, uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_USRIO |= EMAC_USRIO_RMII;
+ } else {
+ p_emac->EMAC_USRIO &= ~EMAC_USRIO_RMII;
+ }
+}
+
+/**
+ * \brief Enable/Disable transceiver input clock.
+ *
+ * \param p_emac Pointer to the EMAC instance.
+ * \param uc_enable 0 to disable transceiver input clock, else to enable it.
+ */
+static inline void emac_enable_transceiver_clock(Emac* p_emac,
+ uint8_t uc_enable)
+{
+ if (uc_enable) {
+ p_emac->EMAC_USRIO |= EMAC_USRIO_CLKEN;
+ } else {
+ p_emac->EMAC_USRIO &= ~EMAC_USRIO_CLKEN;
+ }
+}
+
+uint8_t emac_phy_read(Emac* p_emac, uint8_t uc_phy_address, uint8_t uc_address,
+ uint32_t* p_value);
+uint8_t emac_phy_write(Emac* p_emac, uint8_t uc_phy_address,
+ uint8_t uc_address, uint32_t ul_value);
+void emac_dev_init(Emac* p_emac, emac_device_t* p_emac_dev,
+ emac_options_t* p_opt);
+uint32_t emac_dev_read(emac_device_t* p_emac_dev, uint8_t* p_frame,
+ uint32_t ul_frame_size, uint32_t* p_rcv_size);
+uint32_t emac_dev_write(emac_device_t* p_emac_dev, void *p_buffer,
+ uint32_t ul_size, emac_dev_tx_cb_t func_tx_cb);
+uint32_t emac_dev_get_tx_load(emac_device_t* p_emac_dev);
+void emac_dev_set_rx_callback(emac_device_t* p_emac_dev,
+ emac_dev_tx_cb_t func_rx_cb);
+uint8_t emac_dev_set_tx_wakeup_callback(emac_device_t* p_emac_dev,
+ emac_dev_wakeup_cb_t func_wakeup, uint8_t uc_threshold);
+void emac_dev_reset(emac_device_t* p_emac_dev);
+void emac_handler(emac_device_t* p_emac_dev);
+
+
+/// @cond 0
+/**INDENT-OFF**/
+#ifdef __cplusplus
+}
+#endif
+/**INDENT-ON**/
+/// @endcond
+
+/**
+ * \page emac_quickstart Quickstart guide for EMAC driver.
+ *
+ * This is the quickstart guide for the \ref emac_group "Ethernet MAC",
+ * with step-by-step instructions on how to configure and use the driver in a
+ * selection of use cases.
+ *
+ * The use cases contain several code fragments. The code fragments in the
+ * steps for setup can be copied into a custom initialization function, while
+ * the steps for usage can be copied into, e.g., the main application function.
+ *
+ * \section emac_basic_use_case Basic use case
+ * In the basic use case, the EMAC driver are configured for:
+ * - PHY component DM9161A is used
+ * - EMAC uses RMII mode
+ * - The number of receive buffer is 16
+ * - The number of transfer buffer is 8
+ * - MAC address is set to 00-04-25-1c-a0-02
+ * - IP address is set to 192.168.0.2
+ * - IP address is set to 192.168.0.2
+ * - Gateway is set to 192.168.0.1
+ * - Network mask is 255.255.255.0
+ * - PHY operation max retry count is 1000000
+ * - EMAC is configured to not support copy all frame and support broadcast
+ * - The reset PIN of DM9161A is connected to the NRST of SAM3X
+ * - The data will be read from the ethernet
+ *
+ * \section emac_basic_use_case_setup Setup steps
+ *
+ * \subsection emac_basic_use_case_setup_prereq Prerequisites
+ * -# \ref sysclk_group "System Clock Management (sysclock)"
+ * -# \ref pio_group "Parallel Input/Output Controller (pio)"
+ * -# \ref pmc_group "Power Management Controller (pmc)"
+ * -# \ref sam_drivers_rstc_group "Reset Controller (RSTC)"
+ * -# \ref dm9161a_ethernet_phy_group "PHY component (DM9161A)"
+ *
+ * \subsection emac_basic_use_case_setup_code Example code
+ * Content of conf_eth.h
+ * \code
+ * #define EMAC_RX_BUFFERS 16
+ * #define EMAC_TX_BUFFERS 8
+ * #define MAC_PHY_RETRY_MAX 1000000
+ * #define ETHERNET_CONF_ETHADDR0 0x00
+ * #define ETHERNET_CONF_ETHADDR0 0x00
+ * #define ETHERNET_CONF_ETHADDR1 0x04
+ * #define ETHERNET_CONF_ETHADDR2 0x25
+ * #define ETHERNET_CONF_ETHADDR3 0x1C
+ * #define ETHERNET_CONF_ETHADDR4 0xA0
+ * #define ETHERNET_CONF_ETHADDR5 0x02
+ * #define ETHERNET_CONF_IPADDR0 192
+ * #define ETHERNET_CONF_IPADDR1 168
+ * #define ETHERNET_CONF_IPADDR2 0
+ * #define ETHERNET_CONF_IPADDR3 2
+ * #define ETHERNET_CONF_GATEWAY_ADDR0 192
+ * #define ETHERNET_CONF_GATEWAY_ADDR1 168
+ * #define ETHERNET_CONF_GATEWAY_ADDR2 0
+ * #define ETHERNET_CONF_GATEWAY_ADDR3 1
+ * #define ETHERNET_CONF_NET_MASK0 255
+ * #define ETHERNET_CONF_NET_MASK1 255
+ * #define ETHERNET_CONF_NET_MASK2 255
+ * #define ETHERNET_CONF_NET_MASK3 0
+ * #define ETH_PHY_MODE BOARD_EMAC_MODE_RMII
+ * \endcode
+ *
+ * A specific emac device and the receive data buffer must be defined; another ul_frm_size should be defined
+ * to trace the actual size of the data received.
+ * \code
+ * static emac_device_t gs_emac_dev;
+ * static volatile uint8_t gs_uc_eth_buffer[EMAC_FRAME_LENTGH_MAX];
+ *
+ * uint32_t ul_frm_size;
+ * \endcode
+ *
+ * Add to application C-file:
+ * \code
+ * void emac_init(void)
+ * {
+ * sysclk_init();
+ *
+ * board_init();
+ *
+ * rstc_set_external_reset(RSTC, 13);
+ * rstc_reset_extern(RSTC);
+ * while (rstc_get_status(RSTC) & RSTC_SR_NRSTL) {
+ * };
+ *
+ * ul_delay = sysclk_get_cpu_hz() / 1000 / 3 * 400;
+ * while (ul_delay--);
+ *
+ * pmc_enable_periph_clk(ID_EMAC);
+ *
+ * emac_option.uc_copy_all_frame = 0;
+ * emac_option.uc_no_boardcast = 0;
+ * memcpy(emac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address));
+ * gs_emac_dev.p_hw = EMAC;
+ *
+ * emac_dev_init(EMAC, &gs_emac_dev, &emac_option);
+ *
+ * NVIC_EnableIRQ(EMAC_IRQn);
+ *
+ * ethernet_phy_init(EMAC, BOARD_EMAC_PHY_ADDR, sysclk_get_cpu_hz()
+ *
+ * ethernet_phy_auto_negotiate(EMAC, BOARD_EMAC_PHY_ADDR
+ *
+ * ethernet_phy_set_link(EMAC, BOARD_EMAC_PHY_ADDR, 1)
+ * \endcode
+ *
+ * \subsection emac_basic_use_case_setup_flow Workflow
+ * -# Ensure that conf_emac.h is present and contains the
+ * following configuration symbol. This configuration file is used
+ * by the driver and should not be included by the user.
+ * - \code
+ * #define EMAC_RX_BUFFERS 16
+ * #define EMAC_TX_BUFFERS 8
+ * #define MAC_PHY_RETRY_MAX 1000000
+ * #define ETHERNET_CONF_ETHADDR0 0x00
+ * #define ETHERNET_CONF_ETHADDR0 0x00
+ * #define ETHERNET_CONF_ETHADDR1 0x04
+ * #define ETHERNET_CONF_ETHADDR2 0x25
+ * #define ETHERNET_CONF_ETHADDR3 0x1C
+ * #define ETHERNET_CONF_ETHADDR4 0xA0
+ * #define ETHERNET_CONF_ETHADDR5 0x02
+ * #define ETHERNET_CONF_IPADDR0 192
+ * #define ETHERNET_CONF_IPADDR1 168
+ * #define ETHERNET_CONF_IPADDR2 0
+ * #define ETHERNET_CONF_IPADDR3 2
+ * #define ETHERNET_CONF_GATEWAY_ADDR0 192
+ * #define ETHERNET_CONF_GATEWAY_ADDR1 168
+ * #define ETHERNET_CONF_GATEWAY_ADDR2 0
+ * #define ETHERNET_CONF_GATEWAY_ADDR3 1
+ * #define ETHERNET_CONF_NET_MASK0 255
+ * #define ETHERNET_CONF_NET_MASK1 255
+ * #define ETHERNET_CONF_NET_MASK2 255
+ * #define ETHERNET_CONF_NET_MASK3 0
+ * #define ETH_PHY_MODE BOARD_EMAC_MODE_RMII
+ * \endcode
+ * -# Enable the system clock:
+ * - \code sysclk_init(); \endcode
+ * -# Enable PIO configurations for EMAC:
+ * - \code board_init(); \endcode
+ * -# Reset PHY; this is required by the DM9161A component:
+ * - \code
+ * rstc_set_external_reset(RSTC, 13);
+ * rstc_reset_extern(RSTC);
+ * while (rstc_get_status(RSTC) & RSTC_SR_NRSTL) {
+ * };
+ * \endcode
+ * -# Wait for PHY ready:
+ * - \code
+ * ul_delay = sysclk_get_cpu_hz() / 1000 / 3 * 400;
+ * while (ul_delay--);
+ * \endcode
+ * -# Enable PMC clock for EMAC:
+ * - \code pmc_enable_periph_clk(ID_EMAC); \endcode
+ * -# Set the EMAC options; it's set to copy all frame and support broadcast:
+ * - \code
+ * emac_option.uc_copy_all_frame = 0;
+ * emac_option.uc_no_boardcast = 0;
+ * memcpy(emac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address));
+ * gs_emac_dev.p_hw = EMAC;
+ * \endcode
+ * -# Initialize EMAC device with the filled option:
+ * - \code
+ * emac_dev_init(EMAC, &gs_emac_dev, &emac_option);
+ * \endcode
+ * -# Enable the interrupt service for EMAC:
+ * - \code
+ * NVIC_EnableIRQ(EMAC_IRQn);
+ * \endcode
+ * -# Initialize the PHY component:
+ * - \code
+ * ethernet_phy_init(EMAC, BOARD_EMAC_PHY_ADDR, sysclk_get_cpu_hz());
+ * \endcode
+ * -# The link will be established based on auto negotiation.
+ * - \code
+ * ethernet_phy_auto_negotiate(EMAC, BOARD_EMAC_PHY_ADDR);
+ * \endcode
+ * -# Establish the ethernet link; the network can be worked from now on:
+ * - \code
+ * ethernet_phy_set_link(EMAC, BOARD_EMAC_PHY_ADDR, 1);
+ * \endcode
+ *
+ * \section emac_basic_use_case_usage Usage steps
+ * \subsection emac_basic_use_case_usage_code Example code
+ * Add to, e.g., main loop in application C-file:
+ * \code
+ * emac_dev_read(&gs_emac_dev, (uint8_t *) gs_uc_eth_buffer, sizeof(gs_uc_eth_buffer), &ul_frm_size));
+ * \endcode
+ *
+ * \subsection emac_basic_use_case_usage_flow Workflow
+ * -# Start reading the data from the ethernet:
+ * - \code emac_dev_read(&gs_emac_dev, (uint8_t *) gs_uc_eth_buffer, sizeof(gs_uc_eth_buffer), &ul_frm_size)); \endcode
+ */
+
+#endif /* EMAC_H_INCLUDED */
diff --git a/Libraries/EMAC/ethernet_phy.h b/Libraries/EMAC/ethernet_phy.h
index 378e688d..64c565a4 100644
--- a/Libraries/EMAC/ethernet_phy.h
+++ b/Libraries/EMAC/ethernet_phy.h
@@ -50,7 +50,7 @@
//#include "board.h"
//#include "emac.h"
#include "Arduino.h"
-#include <include/emac.h>
+#include "emac.h"
#include "conf_eth.h"
/// @cond 0
diff --git a/Libraries/EMAC/ethernet_sam.c b/Libraries/EMAC/ethernet_sam.c
index 90c30b86..c1827b46 100644
--- a/Libraries/EMAC/ethernet_sam.c
+++ b/Libraries/EMAC/ethernet_sam.c
@@ -66,20 +66,10 @@
#include "lwip/src/include/lwip/tcp_impl.h"
#endif
#include "lwip/src/include/netif/etharp.h"
-#include "lwip/src/sam/include/netif/ethernetif.h"
-
-#include "include/emac.h"
-
-extern void RepRapNetworkMessage(const char*);
+#include "ethernetif.h"
/* Global variable containing MAC Config (hw addr, IP, GW, ...) */
struct netif gs_net_if;
-static bool net_if_ready = false;
-
-struct netif* ethernet_get_configuration()
-{
- return &gs_net_if;
-}
/* Timer for calling lwIP tmr functions without system */
typedef struct timers_info {
@@ -136,11 +126,12 @@ void ethernet_timers_update(void)
}
}
+
//************************************************************************************************************
// Added by AB.
-static void ethernet_configure_interface(const uint8_t ipAddress[], const uint8_t netMask[], const uint8_t gateWay[])
+void start_ethernet(const uint8_t ipAddress[], const uint8_t netMask[], const uint8_t gateWay[], netif_status_callback_fn status_cb)
{
struct ip_addr x_ip_addr, x_net_mask, x_gateway;
extern err_t ethernetif_init(struct netif *netif);
@@ -165,17 +156,17 @@ static void ethernet_configure_interface(const uint8_t ipAddress[], const uint8_
netif_set_default(&gs_net_if);
/* Setup callback function for netif status change */
- netif_set_status_callback(&gs_net_if, ethernet_status_callback);
+ netif_set_status_callback(&gs_net_if, status_cb);
/* Bring it up */
if (x_ip_addr.addr == 0)
{
- RepRapNetworkMessage("Starting DHCP...\n");
+ /* DHCP mode */
dhcp_start(&gs_net_if);
}
else
{
- RepRapNetworkMessage("Starting network...\n");
+ /* Static mode */
netif_set_up(&gs_net_if);
}
}
@@ -188,11 +179,6 @@ void ethernet_set_configuration(const uint8_t ipAddress[], const uint8_t netMask
IP4_ADDR(&x_net_mask, netMask[0], netMask[1], netMask[2], netMask[3]);
IP4_ADDR(&x_gateway, gateWay[0], gateWay[1], gateWay[2], gateWay[3]);
- if (gs_net_if.ip_addr.addr == 0)
- {
- dhcp_stop(&gs_net_if);
- }
-
if (x_ip_addr.addr == 0)
{
dhcp_start(&gs_net_if);
@@ -225,25 +211,7 @@ bool establish_ethernet_link(void)
return ethernet_establish_link(); // this is the one that takes a long time
}
-/** \brief Create ethernet task, for ethernet management.
- *
- */
-void start_ethernet(const unsigned char ipAddress[], const unsigned char netMask[], const unsigned char gateWay[])
-{
- /* Set hw and IP parameters, initialize MAC too */
- ethernet_configure_interface(ipAddress, netMask, gateWay);
-}
-
-/** \brief Check if the ethernet interface has been configured completely and if
- * an IP has been assigned to it.
- *
- */
-bool ethernet_is_ready()
-{
- return net_if_ready;
-}
-
-//*************************************************************************************************************
+#if 0
/**
* \brief Status callback used to print address given by DHCP.
*
@@ -256,7 +224,6 @@ void ethernet_status_callback(struct netif *netif)
{
RepRapNetworkMessage("Network up, IP=");
ipaddr_ntoa_r(&(netif->ip_addr), c_mess, sizeof(c_mess));
- c_mess[sizeof(c_mess) - 1] = 0; // ensure null terminated
strncat(c_mess, "\n\n", sizeof(c_mess) - strlen(c_mess) - 1);
RepRapNetworkMessage(c_mess);
netif->flags |= NETIF_FLAG_LINK_UP;
@@ -267,23 +234,21 @@ void ethernet_status_callback(struct netif *netif)
RepRapNetworkMessage("Network down\n");
}
}
+#endif
-
-/**0
+/**
* \brief Manage the Ethernet packets, if any received process them.
* After processing any packets, manage the lwIP timers.
*
* \return Returns true if data has been processed.
*/
-bool ethernet_read(void)
+void ethernet_task(void)
{
/* Run polling tasks */
- bool data_read = ethernetif_input(&gs_net_if);
+ while (ethernetif_input(&gs_net_if));
/* Run periodic tasks */
ethernet_timers_update();
-
- return data_read;
}
/*
@@ -297,3 +262,19 @@ void ethernet_set_rx_callback(emac_dev_tx_cb_t callback)
{
ethernetif_set_rx_callback(callback);
}
+
+/*
+ * \brief Sets the network adapter's MAC address.
+ */
+void ethernet_set_mac_address(const uint8_t macAddress[])
+{
+ ethernetif_set_mac_address(macAddress);
+}
+
+/*
+ * \brief Returns the current IP address
+ */
+const uint8_t *ethernet_get_ipaddress()
+{
+ return (uint8_t*)&gs_net_if.ip_addr.addr;
+}
diff --git a/Libraries/EMAC/ethernet_sam.h b/Libraries/EMAC/ethernet_sam.h
index eac055b0..c8f335fb 100644
--- a/Libraries/EMAC/ethernet_sam.h
+++ b/Libraries/EMAC/ethernet_sam.h
@@ -45,6 +45,7 @@
#define ETHERNET_SAM_H_INCLUDED
#include <lwip/src/include/lwip/netif.h>
+#include "emac.h"
/// @cond 0
/**INDENT-OFF**/
@@ -54,45 +55,32 @@ extern "C" {
/**INDENT-ON**/
/// @endcond
-/**
- * \brief Initialize the ethernet interface.
- *
- */
+
+// Perform low-level initialisation of the network interface
void init_ethernet(const uint8_t macAddress[], const char *hostname);
-bool establish_ethernet_link(void);
-void start_ethernet(const uint8_t ipAddress[], const uint8_t netMask[], const uint8_t gateWay[]);
-bool ethernet_is_ready();
+// Perform ethernet auto-negotiation and establish link. Returns true when ready
+bool ethernet_establish_link(void);
-struct netif* ethernet_get_configuration();
-void ethernet_set_configuration(const uint8_t ipAddress[], const uint8_t netMask[], const uint8_t gateWay[]);
+// Initialise network interface and set network interface status callback
+void start_ethernet(const uint8_t ipAddress[], const uint8_t netMask[], const uint8_t gateWay[], netif_status_callback_fn status_cb);
-void ethernet_timers_update(void);
+// Update IPv4 configuration on demand
+void ethernet_set_configuration(const unsigned char ipAddress[], const unsigned char netMask[], const unsigned char gateWay[]);
-/**
- * \brief Status callback used to print address given by DHCP.
- *
- * \param netif Instance to network interface.
- *
- */
-void ethernet_status_callback(struct netif *netif);
+// Must be called periodically to keep the LwIP timers running
+void ethernet_timers_update(void);
-/**
- * \brief Manage the ethernet packets, if any received process them.
- *
- * \return Returns true if data has been processed.
- */
-bool ethernet_read(void);
+// Reads all stored network packets and processes them
+void ethernet_task(void);
-/*
- * \brief Sets the EMAC RX callback. It will be called when a new packet
- * can be processed and should be called with a NULL parameter inside
- * the actual callback.
- *
- * \param callback The callback to be called when a new packet is ready
- */
+// Set the RX callback for incoming network packets
void ethernet_set_rx_callback(emac_dev_tx_cb_t callback);
+// Returns the network interface's current IPv4 address
+const uint8_t *ethernet_get_ipaddress();
+
+
/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
diff --git a/Libraries/Lwip/lwip/src/sam/netif/ethernetif.c b/Libraries/EMAC/ethernetif.c
index e44397c3..c4c5213f 100644
--- a/Libraries/Lwip/lwip/src/sam/netif/ethernetif.c
+++ b/Libraries/EMAC/ethernetif.c
@@ -55,20 +55,11 @@
#include "lwip/src/include/netif/etharp.h"
#include "lwip/src/include/netif/ppp_oe.h"
-#if 0
-// DC 2015-07-28.
-// The original RepRapPro code has the source files included like this, instead of the header files.
-// I tried including the header files instead, and found that doing so makes file upload unreliable. So leave it like this.
-#include "source/emac.c"
-#include "source/rstc.c"
-#else
-// DC 2015-07-28: this doesn't work, see above.
-//#include "include/emac.h"
-//#include "include/rstc.h"
-#endif
+#include "emac.h"
+#include "include/rstc.h"
#include "ethernet_phy.h"
-#include "lwip/src/sam/include/netif/ethernetif.h"
+#include "ethernetif.h"
#include <string.h>
#include "conf_eth.h"
@@ -135,17 +126,6 @@ void EMAC_Handler(void)
emac_handler(&gs_emac_dev);
}
-static bool ResettingEther()
-{
- return (rstc_get_status(RSTC) & RSTC_SR_NRSTL);
-}
-
-void ResetEther()
-{
- rstc_set_external_reset(RSTC, 15);
- rstc_reset_extern(RSTC);
-}
-
/**
* \brief In this function, the hardware should be initialized.
* Called from ethernetif_init().
@@ -192,6 +172,9 @@ static void low_level_init(struct netif *netif)
#if defined(DHCP_USED)
| NETIF_FLAG_DHCP
#endif
+#if LWIP_IGMP
+ | NETIF_FLAG_IGMP
+#endif
;
netif->flags |= NETIF_FLAG_LINK_UP; //the link is up?
@@ -217,38 +200,30 @@ static void low_level_init(struct netif *netif)
void ethernet_hardware_init(void)
{
/* Reset PHY */
- //rstc_set_external_reset(RSTC, 13); /* (2^(13+1))/32768 */
-// rstc_set_external_reset(RSTC, 15);
-// rstc_reset_extern(RSTC);
-// while (rstc_get_status(RSTC) & RSTC_SR_NRSTL) {
-// }
-
-// ResetEther();
- while(ResettingEther()){}
+ rstc_set_external_reset(RSTC, 13); /* (2^(13+1))/32768 */
+ rstc_reset_extern(RSTC);
+ while (rstc_get_status(RSTC) & RSTC_SR_NRSTL);
+ rstc_set_external_reset(RSTC, 0); /* restore default */
/* Wait for PHY to be ready (CAT811: Max400ms) */
- {
- volatile u32_t ul_dealy = SystemCoreClock / 6;
- while (ul_dealy--) { }
- }
+ volatile u32_t ul_dealy = SystemCoreClock / 1000 / 3 * 400;
+ while (ul_dealy--) { }
/* Enable EMAC clock */
pmc_enable_periph_clk(ID_EMAC);
- {
- emac_options_t emac_option;
+ emac_options_t emac_option;
- /* Fill in EMAC options */
- emac_option.uc_copy_all_frame = 1;
- emac_option.uc_no_boardcast = 0;
+ /* Fill in EMAC options */
+ emac_option.uc_copy_all_frame = 1;
+ emac_option.uc_no_boardcast = 0;
- memcpy(emac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address));
+ memcpy(emac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address));
- gs_emac_dev.p_hw = EMAC;
+ gs_emac_dev.p_hw = EMAC;
- /* Init EMAC driver structure */
- emac_dev_init(EMAC, &gs_emac_dev, &emac_option);
- }
+ /* Init EMAC driver structure */
+ emac_dev_init(EMAC, &gs_emac_dev, &emac_option);
/* Set IRQ priority */
NVIC_SetPriority(EMAC_IRQn, 4);
@@ -266,12 +241,14 @@ void ethernet_hardware_init(void)
bool ethernet_establish_link(void)
{
/* Auto Negotiate, work in RMII mode */
- if (ethernet_phy_auto_negotiate(EMAC, BOARD_EMAC_PHY_ADDR) != EMAC_OK) {
+ if (ethernet_phy_auto_negotiate(EMAC, BOARD_EMAC_PHY_ADDR) != EMAC_OK)
+ {
return false;
}
/* Establish ethernet link */
- if (ethernet_phy_set_link(EMAC, BOARD_EMAC_PHY_ADDR, 1) != EMAC_OK) {
+ if (ethernet_phy_set_link(EMAC, BOARD_EMAC_PHY_ADDR, 1) != EMAC_OK)
+ {
LWIP_DEBUGF(LWIP_DBG_TRACE,("Set link ERROR!\r"));
return false;
}
@@ -509,3 +486,8 @@ void ethernetif_set_mac_address(const u8_t macAddress[])
gs_uc_mac_address[i] = macAddress[i];
}
}
+
+u32_t sys_now(void)
+{
+ return millis();
+}
diff --git a/Libraries/Lwip/lwip/src/sam/include/netif/ethernetif.h b/Libraries/EMAC/ethernetif.h
index d39091dd..d39091dd 100644
--- a/Libraries/Lwip/lwip/src/sam/include/netif/ethernetif.h
+++ b/Libraries/EMAC/ethernetif.h
diff --git a/Libraries/EMAC/examples/Arduino_Due_EMAC_Mod/Arduino_Due_EMAC_Mod.ino b/Libraries/EMAC/examples/Arduino_Due_EMAC_Mod/Arduino_Due_EMAC_Mod.ino
deleted file mode 100644
index 88105633..00000000
--- a/Libraries/EMAC/examples/Arduino_Due_EMAC_Mod/Arduino_Due_EMAC_Mod.ino
+++ /dev/null
@@ -1,369 +0,0 @@
-// Arduino Due - EMAC Sample 1
-// Brief EMAC example for Arduino Due
-// This example demonstrates how to configure and use
-// the EMAC peripheral.
-// By Wilfredo Molina @2013
-
-// Required libraries
-#include "variant.h"
-#include <conf_eth.h>
-#include <mini_ip.h>
-#include <ethernet_phy.h>
-#include <rmii.h>
-#include <include/emac.h>
-//#include <source/emac.c>
-#include <include/rstc.h>
-//#include <source/rstc.c>
-
-void setup()
-{
- // start serial port at 9600 bps:
- SerialUSB.begin(115200);
- while (!SerialUSB.available());
-}
-
-// The MAC address used for the test
-static uint8_t gs_uc_mac_address[] =
- { ETHERNET_CONF_ETHADDR0, ETHERNET_CONF_ETHADDR1, ETHERNET_CONF_ETHADDR2,
- ETHERNET_CONF_ETHADDR3, ETHERNET_CONF_ETHADDR4, ETHERNET_CONF_ETHADDR5
-};
-
-// The IP address used for test (ping ...)
-static uint8_t gs_uc_ip_address[] =
- { ETHERNET_CONF_IPADDR0, ETHERNET_CONF_IPADDR1,
- ETHERNET_CONF_IPADDR2, ETHERNET_CONF_IPADDR3 };
-
-// The EMAC driver instance
-static emac_device_t gs_emac_dev;
-
-// Buffer for ethernet packets
-static volatile uint8_t gs_uc_eth_buffer[EMAC_FRAME_LENTGH_MAX];
-
-/**
- * \brief Process & return the ICMP checksum.
- *
- * \param p_buff Pointer to the buffer.
- * \param ul_len The length of the buffered data.
- *
- * \return Checksum of the ICMP.
- */
-static uint16_t emac_icmp_checksum(uint16_t *p_buff, uint32_t ul_len)
-{
- uint32_t i, ul_tmp;
-
- for (i = 0, ul_tmp = 0; i < ul_len; i++, p_buff++) {
-
- ul_tmp += SWAP16(*p_buff);
- }
- ul_tmp = (ul_tmp & 0xffff) + (ul_tmp >> 16);
-
- return (uint16_t) (~ul_tmp);
-}
-
-/**
- * \brief Display the IP packet.
- *
- * \param p_ip_header Pointer to the IP header.
- * \param ul_size The data size.
- */
-static void emac_display_ip_packet(p_ip_header_t p_ip_header, uint32_t ul_size)
-{
-/* printf("======= IP %4d bytes, HEADER ==========\n\r", (int)ul_size);
- printf(" IP Version = v.%d", (p_ip_header->ip_hl_v & 0xF0) >> 4);
- printf("\n\r Header Length = %d", p_ip_header->ip_hl_v & 0x0F);
- printf("\n\r Type of service = 0x%x", p_ip_header->ip_tos);
- printf("\n\r Total IP Length = 0x%X",
- (((p_ip_header->ip_len) >> 8) & 0xff) +
- (((p_ip_header->ip_len) << 8) & 0xff00));
- printf("\n\r ID = 0x%X",
- (((p_ip_header->ip_id) >> 8) & 0xff) +
- (((p_ip_header->ip_id) << 8) & 0xff00));
- printf("\n\r Header Checksum = 0x%X",
- (((p_ip_header->ip_sum) >> 8) & 0xff) +
- (((p_ip_header->ip_sum) << 8) & 0xff00));
-*/ SerialUSB.println("\r Protocol = ");
-
- switch (p_ip_header->ip_p) {
- case IP_PROT_ICMP:
- SerialUSB.println("ICMP");
- break;
-
- case IP_PROT_IP:
- SerialUSB.println("IP");
- break;
-
- case IP_PROT_TCP:
- SerialUSB.println("TCP");
- break;
-
- case IP_PROT_UDP:
- SerialUSB.println("UDP");
- break;
-
- default:
-// printf("%d (0x%X)", p_ip_header->ip_p, p_ip_header->ip_p);
- break;
- }
-
-/* printf("\n\r IP Src Address = %d:%d:%d:%d",
- p_ip_header->ip_src[0],
- p_ip_header->ip_src[1],
- p_ip_header->ip_src[2], p_ip_header->ip_src[3]);
-
- printf("\n\r IP Dest Address = %d:%d:%d:%d",
- p_ip_header->ip_dst[0],
- p_ip_header->ip_dst[1],
- p_ip_header->ip_dst[2], p_ip_header->ip_dst[3]);
-*/ SerialUSB.println("\n\r----------------------------------------\r");
-}
-
-/**
- * \brief Process the received ARP packet; change address and send it back.
- *
- * \param p_uc_data The data to process.
- * \param ul_size The data size.
- */
-static void emac_process_arp_packet(uint8_t *p_uc_data, uint32_t ul_size)
-{
- uint32_t i;
- uint8_t ul_rc = EMAC_OK;
-
- p_ethernet_header_t p_eth = (p_ethernet_header_t) p_uc_data;
- p_arp_header_t p_arp = (p_arp_header_t) (p_uc_data + ETH_HEADER_SIZE);
-
- if (SWAP16(p_arp->ar_op) == ARP_REQUEST) {
-/* printf("-- IP %d.%d.%d.%d\n\r",
- p_eth->et_dest[0], p_eth->et_dest[1],
- p_eth->et_dest[2], p_eth->et_dest[3]);
-
- printf("-- IP %d.%d.%d.%d\n\r",
- p_eth->et_src[0], p_eth->et_src[1],
- p_eth->et_src[2], p_eth->et_src[3]);
-*/
- // ARP reply operation
- p_arp->ar_op = SWAP16(ARP_REPLY);
-
- // Fill the destination address and source address
- for (i = 0; i < 6; i++) {
- // Swap ethernet destination address and ethernet source address
- p_eth->et_dest[i] = p_eth->et_src[i];
- p_eth->et_src[i] = gs_uc_mac_address[i];
- p_arp->ar_tha[i] = p_arp->ar_sha[i];
- p_arp->ar_sha[i] = gs_uc_mac_address[i];
- }
- // Swap the source IP address and the destination IP address
- for (i = 0; i < 4; i++) {
- p_arp->ar_tpa[i] = p_arp->ar_spa[i];
- p_arp->ar_spa[i] = gs_uc_ip_address[i];
- }
- ul_rc = emac_dev_write(&gs_emac_dev, p_uc_data, ul_size, NULL);
- if (ul_rc != EMAC_OK) {
- printf("E: ARP Send - 0x%x\n\r", ul_rc);
- }
- }
-}
-
-/**
- * \brief Process the received IP packet; change address and send it back.
- *
- * \param p_uc_data The data to process.
- * \param ul_size The data size.
- */
-static void emac_process_ip_packet(uint8_t *p_uc_data, uint32_t ul_size)
-{
- uint32_t i;
- uint32_t ul_icmp_len;
- int32_t ul_rc = EMAC_OK;
-
- ul_size = ul_size; // stop warning
-
- p_ethernet_header_t p_eth = (p_ethernet_header_t) p_uc_data;
- p_ip_header_t p_ip_header = (p_ip_header_t) (p_uc_data + ETH_HEADER_SIZE);
-
- p_icmp_echo_header_t p_icmp_echo =
- (p_icmp_echo_header_t) ((int8_t *) p_ip_header +
- ETH_IP_HEADER_SIZE);
-/* printf("-- IP %d.%d.%d.%d\n\r", p_eth->et_dest[0], p_eth->et_dest[1],
- p_eth->et_dest[2], p_eth->et_dest[3]);
-
- printf("-- IP %d.%d.%d.%d\n\r",
- p_eth->et_src[0], p_eth->et_src[1], p_eth->et_src[2],
- p_eth->et_src[3]);
-*/ switch (p_ip_header->ip_p) {
- case IP_PROT_ICMP:
- if (p_icmp_echo->type == ICMP_ECHO_REQUEST) {
- p_icmp_echo->type = ICMP_ECHO_REPLY;
- p_icmp_echo->code = 0;
- p_icmp_echo->cksum = 0;
-
- // Checksum of the ICMP message
- ul_icmp_len = (SWAP16(p_ip_header->ip_len) - ETH_IP_HEADER_SIZE);
- if (ul_icmp_len % 2) {
- *((uint8_t *) p_icmp_echo + ul_icmp_len) = 0;
- ul_icmp_len++;
- }
- ul_icmp_len = ul_icmp_len / sizeof(uint16_t);
-
- p_icmp_echo->cksum = SWAP16(
- emac_icmp_checksum((uint16_t *)p_icmp_echo, ul_icmp_len));
- // Swap the IP destination address and the IP source address
- for (i = 0; i < 4; i++) {
- p_ip_header->ip_dst[i] =
- p_ip_header->ip_src[i];
- p_ip_header->ip_src[i] = gs_uc_ip_address[i];
- }
- // Swap ethernet destination address and ethernet source address
- for (i = 0; i < 6; i++) {
- // Swap ethernet destination address and ethernet source address
- p_eth->et_dest[i] = p_eth->et_src[i];
- p_eth->et_src[i] = gs_uc_mac_address[i];
- }
- // Send the echo_reply
- ul_rc = emac_dev_write(&gs_emac_dev, p_uc_data,
- SWAP16(p_ip_header->ip_len) + 14, NULL);
- if (ul_rc != EMAC_OK) {
-// printf("E: ICMP Send - 0x%x\n\r", ul_rc);
- }
- }
- break;
-
- default:
- break;
- }
-}
-
-/**
- * \brief Process the received EMAC packet.
- *
- * \param p_uc_data The data to process.
- * \param ul_size The data size.
- */
-static void emac_process_eth_packet(uint8_t *p_uc_data, uint32_t ul_size)
-{
- uint16_t us_pkt_format;
-
- p_ethernet_header_t p_eth = (p_ethernet_header_t) (p_uc_data);
- p_ip_header_t p_ip_header = (p_ip_header_t) (p_uc_data + ETH_HEADER_SIZE);
- ip_header_t ip_header;
- us_pkt_format = SWAP16(p_eth->et_protlen);
-
- switch (us_pkt_format) {
- // ARP Packet format
- case ETH_PROT_ARP:
- // Process the ARP packet
- emac_process_arp_packet(p_uc_data, ul_size);
-
- break;
-
- // IP protocol frame
- case ETH_PROT_IP:
- // Backup the header
- memcpy(&ip_header, p_ip_header, sizeof(ip_header_t));
-
- // Process the IP packet
- emac_process_ip_packet(p_uc_data, ul_size);
-
- // Dump the IP header
- emac_display_ip_packet(&ip_header, ul_size);
- break;
-
- default:
-// printf("=== Default w_pkt_format= 0x%X===\n\r", us_pkt_format);
- break;
- }
-}
-
-//brief EMAC interrupt handler.
-
-void EMAC_Handler(void)
-{
- emac_handler(&gs_emac_dev);
-}
-
-/**
- * \brief EMAC example entry point.
- *
- * \return Unused (ANSI-C compatibility).
- */
-void loop()
-{
-
- uint32_t ul_frm_size;
- volatile uint32_t ul_delay;
- emac_options_t emac_option;
-
- // Display MAC & IP settings
-/* printf("-- MAC %x:%x:%x:%x:%x:%x\n\r",
- gs_uc_mac_address[0], gs_uc_mac_address[1], gs_uc_mac_address[2],
- gs_uc_mac_address[3], gs_uc_mac_address[4], gs_uc_mac_address[5]);
-
- printf("-- IP %d.%d.%d.%d\n\r", gs_uc_ip_address[0], gs_uc_ip_address[1],
- gs_uc_ip_address[2], gs_uc_ip_address[3]);
-*/
- // Reset PHY
- rstc_set_external_reset(RSTC, 13); // (2^(13+1))/32768
- rstc_reset_extern(RSTC);
- SerialUSB.println("resetting PHY...");
- while (rstc_get_status(RSTC) & RSTC_SR_NRSTL) {
- };
-
- // Wait for PHY to be ready (CAT811: Max400ms)
- ul_delay = SystemCoreClock / 1000 / 3 * 400;
- while (ul_delay--);
- SerialUSB.println("PHY ready.");
-
- // Enable EMAC clock
- pmc_enable_periph_clk(ID_EMAC);
-
- // Fill in EMAC options
- emac_option.uc_copy_all_frame = 0;
- emac_option.uc_no_boardcast = 0;
-
- memcpy(emac_option.uc_mac_addr, gs_uc_mac_address, sizeof(gs_uc_mac_address));
-
- gs_emac_dev.p_hw = EMAC;
-
- SerialUSB.println("Init EMAC driver structure\r\n");
- // Init EMAC driver structure
- emac_dev_init(EMAC, &gs_emac_dev, &emac_option);
-
- // Enable Interrupt
- NVIC_EnableIRQ(EMAC_IRQn);
-
- SerialUSB.println("Init MAC PHY driver\r\n");
-
- // Init MAC PHY driver
- if (ethernet_phy_init(EMAC, BOARD_EMAC_PHY_ADDR, SystemCoreClock)
- != EMAC_OK) {
- SerialUSB.println("PHY Initialize ERROR!\r\n");
- //return -1;
- }
-
- // Auto Negotiate, work in RMII mode
- if (ethernet_phy_auto_negotiate(EMAC, BOARD_EMAC_PHY_ADDR) != EMAC_OK) {
-
- SerialUSB.println("Auto Negotiate ERROR!\r\n");
- //return -1;
- }
-
- SerialUSB.println("Establish ethernet link");
- // Establish ethernet link
- while (ethernet_phy_set_link(EMAC, BOARD_EMAC_PHY_ADDR, 1) != EMAC_OK) {
- SerialUSB.println(EMAC_OK);
- SerialUSB.println("Set link ERROR!\r\n");
- //return -1;
- }
-
- while (1) {
- // Process packets
- if (EMAC_OK != emac_dev_read(&gs_emac_dev, (uint8_t *) gs_uc_eth_buffer,
- sizeof(gs_uc_eth_buffer), &ul_frm_size)) {
- continue;
- }
-
- if (ul_frm_size > 0) {
- // Handle input frame
- emac_process_eth_packet((uint8_t *) gs_uc_eth_buffer, ul_frm_size);
- }
- }
-}
diff --git a/Libraries/Lwip/contrib/apps/mdns/mdns_responder.c b/Libraries/Lwip/contrib/apps/mdns/mdns_responder.c
new file mode 100644
index 00000000..1b8cf368
--- /dev/null
+++ b/Libraries/Lwip/contrib/apps/mdns/mdns_responder.c
@@ -0,0 +1,664 @@
+/****************************************************************//**
+ *
+ * @file mdns_responder.c
+ *
+ * @author Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief mdns service discovery
+ * reqirues SO_REUSE=1 and LWIP_IGMP=1
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * 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: Logan Gunthorpe <logang@deltatee.com>
+ *
+ */
+
+#include "mdns_responder.h"
+
+#include "lwipopts.h"
+#include <lwip/src/include/lwip/udp.h>
+#include <lwip/src/include/ipv4/lwip/igmp.h>
+#include <lwip/src/include/lwip/debug.h>
+#include <lwip/src/include/lwip/mem.h>
+
+#ifndef MDNS_DEBUG
+#define MDNS_DEBUG LWIP_DBG_OFF
+#endif
+
+#ifndef MDNS_PORT
+#define MDNS_PORT 5353
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+
+#ifndef ip_set_option
+#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
+#endif
+extern struct netif gs_net_if;
+
+
+#define FLAG_RESP 0x8400
+
+const char *all_services = "\x09_services\x07_dns-sd\x04_udp\x05local";
+
+#define QCLASS_IN 0x0001
+#define CACHE_FLUSH 0x8000
+
+#define QTYPE_A 0x0001
+#define QTYPE_PTR 0x000C
+#define QTYPE_TXT 0x0010
+#define QTYPE_SRV 0x0021
+
+#define DATA_POINTER 0xC000
+
+#define TTL 10*60
+
+static struct mdns_state mdns_state;
+
+static const char dotlocal[] = "\x05local";
+static const int dotlocal_len = sizeof(dotlocal);
+
+struct mdns_header {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t questions;
+ uint16_t answers;
+ uint16_t authorities;
+ uint16_t additionals;
+};
+
+struct record {
+ uint16_t qtype;
+ uint16_t qclass;
+ uint32_t ttl;
+ uint16_t data_length;
+ char data[];
+} __attribute__((__packed__));
+
+struct srv_record {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ char target[];
+} __attribute__((__packed__));
+
+struct mdns_state {
+ char *hostnames[4];
+ char *service_host;
+ const struct mdns_service *services;
+ int num_services;
+ char *txt_records;
+ struct udp_pcb *sendpcb;
+ struct netif *netif;
+};
+
+static struct pbuf *populate_header(int answers, int authorities,
+ int additionals)
+{
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct mdns_header),
+ PBUF_RAM);
+
+ struct mdns_header *hdr = (struct mdns_header *) p->payload;
+ hdr->id = 0;
+ hdr->flags = htons(FLAG_RESP);
+ hdr->questions = 0;
+ hdr->answers = htons(answers);
+ hdr->authorities = htons(authorities);
+ hdr->additionals = htons(additionals);
+
+ return p;
+}
+
+static int special_strlen(const char *name)
+{
+ int ret = 0;
+
+ while (1) {
+ ret++;
+ int x = *name++;
+ if (x == 0)
+ return ret;
+ if (x & 0xC0)
+ return ret + 1;
+
+ ret +=x;
+ name += x;
+ }
+}
+
+static int special_strcpy(char * dest, const char *name,
+ const struct pbuf *p)
+{
+ int ret = 0;
+ int link_ret = -1;
+
+ const char *end = ((char *) p->payload) + p->len;
+
+ while (1) {
+ int x = *name++;
+ ret++;
+
+ if (name > end) {
+ *dest = 0;
+ return -1;
+ }
+
+ if (x & 0xC0) {
+ ret++;
+ if (link_ret < 0) link_ret = ret;
+ x = (x << 8) | *name++;
+ name = &((char *)p->payload)[x & 0x3FFF];
+ continue;
+ }
+
+ *dest++ = x;
+
+ if (x == 0)
+ return (link_ret >= 0) ? link_ret : ret;
+
+ ret +=x;
+ memcpy(dest, name, x);
+ name += x;
+ dest += x;
+ }
+}
+
+static uint16_t populate_record(const char *name,
+ uint16_t qtype, uint16_t qclass,
+ uint32_t ttl, const void *data,
+ uint16_t datalen,
+ struct pbuf *dest)
+{
+ int title_len = special_strlen(name);
+ int msglen = title_len + sizeof(struct record) + datalen;
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, msglen, PBUF_RAM);
+
+ memcpy(p->payload, name, title_len);
+ char *end = ((char *)p->payload) + title_len;
+
+ struct record *rec = (struct record *) end;
+ rec->qtype = htons(qtype);
+ rec->qclass = htons(qclass);
+ rec->ttl = htonl(ttl);
+ rec->data_length = htons(datalen);
+ memcpy(rec->data, data, datalen);
+
+ uint16_t ret = dest->tot_len + title_len + sizeof(*rec);
+
+ pbuf_cat(dest, p);
+
+ return htons(DATA_POINTER | ret);
+}
+
+static void send_a_record(struct mdns_state *ms, const char *name)
+{
+ struct pbuf *hdr = populate_header(1, 0, 0);
+
+ populate_record(name, QTYPE_A, QCLASS_IN,
+ TTL, &ms->netif->ip_addr,
+ sizeof(ms->netif->ip_addr),
+ hdr);
+
+ udp_send(ms->sendpcb, hdr);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: sending A response\n"));
+
+ pbuf_free(hdr);
+}
+
+static void send_all_services(struct mdns_state *ms, const char *name)
+{
+ struct pbuf *hdr = populate_header(ms->num_services, 0, 0);
+
+ uint16_t first_ptr = htons(DATA_POINTER | hdr->len);
+
+ for (int i = 0; i < ms->num_services; i++) {
+ if (i != 0)
+ name = (char *) &first_ptr;
+ populate_record(name, QTYPE_PTR, QCLASS_IN,
+ TTL, ms->services[i].name,
+ strlen(ms->services[i].name)+1,
+ hdr);
+ }
+
+ udp_send(ms->sendpcb, hdr);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: sending ALL response\n"));
+
+ pbuf_free(hdr);
+
+}
+
+static void send_ptr_record(struct mdns_state *ms, const char *domain,
+ int service)
+{
+ struct pbuf *hdr = populate_header(1, 0, 3);
+
+ uint16_t first_ptr = htons(DATA_POINTER | hdr->len);
+
+ //PTR Record
+ int service_len = strlen(ms->service_host);
+ char service_name[service_len + 2];
+ strcpy(service_name, ms->service_host);
+ memcpy(&service_name[service_len], &first_ptr, sizeof(first_ptr));
+ uint16_t srv_ptr = populate_record(domain, QTYPE_PTR, QCLASS_IN,
+ TTL, service_name,
+ sizeof(service_name),
+ hdr);
+
+ //SRV Record
+ char buf[sizeof(struct srv_record) + service_len + dotlocal_len];
+ struct srv_record *srv_rec = (struct srv_record *) buf;
+ srv_rec->priority = htons(50);
+ srv_rec->weight = htons(0);
+ srv_rec->port = htons(ms->services[service].port);
+ memcpy(srv_rec->target, ms->service_host, service_len);
+ memcpy(srv_rec->target + service_len, dotlocal, dotlocal_len);
+ uint16_t arec_ptr = populate_record((char *)&srv_ptr, QTYPE_SRV,
+ QCLASS_IN, TTL,
+ buf, sizeof(buf),
+ hdr);
+
+ arec_ptr = htons(ntohs(arec_ptr) + sizeof(struct srv_record));
+
+ //TXT Record
+ populate_record((char *) &srv_ptr, QTYPE_TXT,
+ QCLASS_IN, TTL,
+ ms->txt_records, strlen(ms->txt_records) + 1,
+ hdr);
+
+ //A Record
+ populate_record((char *) &srv_ptr, QTYPE_A,
+ QCLASS_IN, TTL,
+ &ms->netif->ip_addr,
+ sizeof(ms->netif->ip_addr),
+ hdr);
+
+ udp_send(ms->sendpcb, hdr);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: sending PTR response\n"));
+
+ pbuf_free(hdr);
+}
+
+static void send_srv_record(struct mdns_state *ms, const char *domain,
+ int service)
+{
+ struct pbuf *hdr = populate_header(1, 0, 2);
+
+ uint16_t first_ptr = htons(DATA_POINTER | hdr->len);
+
+ //SRV Record
+ int service_len = strlen(ms->service_host);
+ char buf[sizeof(struct srv_record) + service_len + dotlocal_len];
+ struct srv_record *srv_rec = (struct srv_record *) buf;
+ srv_rec->priority = htons(50);
+ srv_rec->weight = htons(0);
+ srv_rec->port = htons(ms->services[service].port);
+ memcpy(srv_rec->target, ms->service_host, service_len);
+ memcpy(srv_rec->target + service_len, dotlocal, dotlocal_len);
+ uint16_t arec_ptr = populate_record(domain, QTYPE_SRV,
+ QCLASS_IN, TTL,
+ buf, sizeof(buf),
+ hdr);
+
+ arec_ptr = htons(ntohs(arec_ptr) + sizeof(struct srv_record));
+
+ //TXT Record
+ const char txt[1] = "";
+ populate_record((char *) &first_ptr, QTYPE_TXT,
+ QCLASS_IN, TTL,
+ txt, sizeof(txt),
+ hdr);
+
+ //A Record
+ populate_record((char *) &arec_ptr, QTYPE_A,
+ QCLASS_IN, TTL,
+ &ms->netif->ip_addr,
+ sizeof(ms->netif->ip_addr),
+ hdr);
+
+ udp_send(ms->sendpcb, hdr);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: sending SRV response\n"));
+
+ pbuf_free(hdr);
+}
+
+static void send_rev_record(struct mdns_state *ms, const char *domain)
+{
+ struct pbuf *hdr = populate_header(1, 0, 0);
+
+ int service_len = strlen(ms->service_host);
+ char service_name[service_len + dotlocal_len];
+ strcpy(service_name, ms->service_host);
+ strcat(service_name, dotlocal);
+ populate_record(domain, QTYPE_PTR, QCLASS_IN,
+ TTL, service_name,
+ sizeof(service_name),
+ hdr);
+
+ udp_send(ms->sendpcb, hdr);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: sending REV PTR response\n"));
+
+ pbuf_free(hdr);
+}
+
+static int query_hostname(struct mdns_state *ms, const char *domain)
+{
+ for (int i = 0; i < sizeof(ms->hostnames) / sizeof(*ms->hostnames); i++) {
+ if (strcasecmp(domain, ms->hostnames[i]) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int query_ptr(struct mdns_state *ms, const char *domain)
+{
+ for (int i = 0; i < ms->num_services; i++) {
+ if (strcasecmp(domain, ms->services[i].name) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+static int query_service(struct mdns_state *ms, const char *domain)
+{
+ int hostlen = strlen(ms->service_host);
+
+ if (!strncasecmp(domain, ms->service_host, hostlen))
+ return -1;
+
+ return query_ptr(ms, &domain[hostlen]);
+}
+
+static int compare_reverse_ptr(struct mdns_state *ms, char *buf)
+{
+ buf++;
+ if (strtol(buf, &buf, 10) != ip4_addr4(&ms->netif->ip_addr))
+ return 0;
+
+ buf++;
+ if (strtol(buf, &buf, 10) != ip4_addr3(&ms->netif->ip_addr))
+ return 0;
+
+ buf++;
+ if (strtol(buf, &buf, 10) != ip4_addr2(&ms->netif->ip_addr))
+ return 0;
+
+ buf++;
+ if (strtol(buf, &buf, 10) != ip4_addr1(&ms->netif->ip_addr))
+ return 0;
+
+ if(strcmp(buf, "\x07in-addr\04arpa") == 0)
+ return 1;
+
+ return 0;
+}
+
+static int parse_question(struct mdns_state *ms, struct udp_pcb *upcb,
+ char *data, int qlen, struct pbuf *p)
+{
+ int offset = 0;
+
+ char buf[255];
+ offset = special_strcpy(buf, data, p);
+
+ if (offset < 0)
+ return -1;
+
+ int qtype = (data[offset] << 8) | data[offset+1];
+ offset += 2;
+ int qclass = (data[offset] << 8) | data[offset+1];
+ offset += 2;
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: question '%s' type %d class %d\n",
+ &buf[1], qtype, qclass));
+
+ if (qtype == QTYPE_A) {
+ if (query_hostname(ms, buf))
+ send_a_record(ms, buf);
+ } else if (qtype == QTYPE_PTR) {
+ if (strcasecmp(all_services, buf) == 0) {
+ send_all_services(ms, buf);
+ } else if (compare_reverse_ptr(ms, buf)) {
+ send_rev_record(ms, buf);
+ } else {
+ int service;
+ if ((service = query_ptr(ms, buf)) >= 0)
+ send_ptr_record(ms, buf, service);
+ }
+ } else if (qtype == QTYPE_SRV || qtype == QTYPE_TXT) {
+ int service;
+ if ((service = query_service(ms, buf)) >= 0)
+ send_srv_record(ms, buf, service);
+ }
+
+ return offset;
+}
+
+static void recv(void *arg, struct udp_pcb *upcb, struct pbuf *p,
+ ip_addr_t *addr, u16_t port)
+{
+ struct mdns_state *ms = (struct mdns_state *) arg;
+ struct mdns_header *h = (struct mdns_header *) p->payload;
+ char *questions = (char *) &h[1];
+ int qlen = p->len - sizeof(*h);
+
+ h->flags = ntohs(h->flags);
+ h->questions = ntohs(h->questions);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: packet from "));
+ ip_addr_debug_print(MDNS_DEBUG | LWIP_DBG_STATE, addr);
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ (" %04x %04x\n", h->flags, h->questions));
+
+ if (h->id != 0 || h->questions == 0)
+ goto free_and_return;
+
+ for (int i = 0; i < h->questions; i++) {
+ int offset = parse_question(ms, upcb, questions, qlen, p);
+
+ if (offset < 0)
+ goto free_and_return;
+
+ questions += offset;
+ qlen -= offset;
+ if (qlen <= 0)
+ break;
+ }
+
+free_and_return:
+ pbuf_free(p);
+}
+
+
+static void free_hostnames(struct mdns_state *ms)
+{
+ for (int i = 0; i < sizeof(ms->hostnames) / sizeof(*ms->hostnames); i++) {
+ if (ms->hostnames[i] != NULL) {
+ mem_free(ms->hostnames[i]);
+ ms->hostnames[i] = NULL;
+ }
+ }
+
+ if (ms->service_host != NULL) {
+ mem_free(ms->service_host);
+ ms->service_host = NULL;
+ }
+}
+
+static void setup_hostnames(struct mdns_state *ms, struct netif *netif)
+{
+ int hostlen = strlen(netif->hostname);
+
+ ms->hostnames[0] = mem_malloc(hostlen + dotlocal_len + 1);
+ sprintf(ms->hostnames[0], "%c%s%s", hostlen, netif->hostname,
+ dotlocal);
+
+ ms->hostnames[1] = mem_malloc(hostlen + 2 + dotlocal_len + 2);
+ sprintf(ms->hostnames[1], "%c%s-%02X%s", hostlen+3, netif->hostname,
+ netif->hwaddr[5], dotlocal);
+
+ char macaddr[12];
+ sprintf(macaddr, "%02X%02X%02X%02X%02X%02X",
+ netif->hwaddr[0], netif->hwaddr[1], netif->hwaddr[2],
+ netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]);
+ int maclen = strlen(macaddr);
+
+ ms->hostnames[2] = mem_malloc(hostlen + maclen + dotlocal_len + 2);
+ sprintf(ms->hostnames[2], "%c%s-%s%s", maclen+hostlen+1, netif->hostname,
+ macaddr, dotlocal);
+
+ ms->hostnames[3] = mem_malloc(strlen(macaddr) + dotlocal_len + 1);
+ sprintf(ms->hostnames[3], "%c%s%s", maclen, macaddr, dotlocal);
+
+ for (int i = 0; i < sizeof(ms->hostnames) / sizeof(*ms->hostnames); i++) {
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: hostname registered: %s\n",
+ &ms->hostnames[i][1]));
+ }
+
+ ms->service_host = mem_malloc(hostlen + maclen + 3);
+ sprintf(ms->service_host, "%c%s-%s", maclen+hostlen+1, netif->hostname,
+ macaddr);
+
+}
+
+static void setup_txt_records(struct mdns_state *ms, const char *txt_records[])
+{
+ int totlen = 0;
+
+ for (const char **rec = txt_records; *rec != NULL; rec++)
+ totlen += 1 + strlen(*rec);
+
+ ms->txt_records = mem_malloc(totlen+1);
+
+ char *t = ms->txt_records;
+ for (const char **rec = txt_records; *rec != NULL; rec++) {
+ int l = strlen(*rec);
+ *t++ = l;
+ strcpy(t, *rec);
+ t += l;
+ }
+
+}
+
+err_t mdns_responder_init(const struct mdns_service *services,
+ int num_services,
+ const char *txt_records[])
+{
+ err_t ret;
+
+ memset(&mdns_state, 0, sizeof(mdns_state));
+ mdns_state.netif = &gs_net_if;
+
+ setup_hostnames(&mdns_state, &gs_net_if);
+ setup_txt_records(&mdns_state, txt_records);
+
+ mdns_state.services = services;
+ mdns_state.num_services = num_services;
+
+ struct ip_addr ipgroup;
+ IP4_ADDR(&ipgroup, 224, 0, 0, 251);
+
+ mdns_state.sendpcb = udp_new();
+ if (mdns_state.sendpcb == NULL)
+ return ERR_MEM;
+
+ struct udp_pcb *pcb = udp_new();
+ if (pcb == NULL) {
+ udp_remove(mdns_state.sendpcb);
+ return ERR_MEM;
+ }
+
+ if ((ret = igmp_joingroup(IP_ADDR_ANY, &ipgroup)) != ERR_OK)
+ return ret;
+
+ ip_set_option(pcb, SOF_REUSEADDR);
+ ip_set_option(mdns_state.sendpcb, SOF_REUSEADDR);
+
+ if ((ret = udp_bind(pcb, IP_ADDR_ANY, MDNS_PORT)) != ERR_OK)
+ goto error_exit;
+
+ udp_recv(pcb, recv, &mdns_state);
+
+ if ((ret = udp_bind(mdns_state.sendpcb, 0, MDNS_PORT)) != ERR_OK)
+ goto error_exit;
+
+ if ((ret = udp_connect(mdns_state.sendpcb, &ipgroup, MDNS_PORT)) != ERR_OK)
+ goto error_exit;
+
+ return ERR_OK;
+
+error_exit:
+ udp_remove(pcb);
+ udp_remove(mdns_state.sendpcb);
+ return ret;
+
+}
+
+void mdns_update_hostname()
+{
+ free_hostnames(&mdns_state);
+ setup_hostnames(&mdns_state, &gs_net_if);
+}
+
+void mdns_announce()
+{
+ struct mdns_state *ms = &mdns_state;
+ int count = sizeof(ms->hostnames) / sizeof(*ms->hostnames);
+ struct pbuf *hdr = populate_header(count, 0, 0);
+
+ for (int i = 0; i < count; i++) {
+ populate_record(ms->hostnames[i], QTYPE_A, QCLASS_IN | CACHE_FLUSH,
+ TTL, &ms->netif->ip_addr,
+ sizeof(ms->netif->ip_addr),
+ hdr);
+ }
+
+ udp_send(ms->sendpcb, hdr);
+
+ LWIP_DEBUGF(MDNS_DEBUG | LWIP_DBG_STATE,
+ ("mdns: sending announcment\n"));
+
+ pbuf_free(hdr);
+}
diff --git a/Libraries/Lwip/contrib/apps/mdns/mdns_responder.h b/Libraries/Lwip/contrib/apps/mdns/mdns_responder.h
new file mode 100644
index 00000000..df618268
--- /dev/null
+++ b/Libraries/Lwip/contrib/apps/mdns/mdns_responder.h
@@ -0,0 +1,58 @@
+/****************************************************************//**
+ *
+ * @file mdns_responder.h
+ *
+ * @author Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief mdns service discovery
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * 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: Logan Gunthorpe <logang@deltatee.com>
+ *
+ */
+
+#ifndef __APPS_MDNS_RESPONDER_H__
+#define __APPS_MDNS_RESPONDER_H__
+
+#include <lwip/src/include/lwip/err.h>
+
+struct mdns_service {
+ const char *name;
+ int port;
+};
+
+err_t mdns_responder_init(const struct mdns_service *services,
+ int num_services,
+ const char *txt_records[]);
+
+void mdns_update_hostname();
+void mdns_announce();
+
+#endif
diff --git a/Libraries/Lwip/lwip/CHANGELOG b/Libraries/Lwip/lwip/CHANGELOG
index 6e27a66b..af68299e 100644
--- a/Libraries/Lwip/lwip/CHANGELOG
+++ b/Libraries/Lwip/lwip/CHANGELOG
@@ -12,6 +12,305 @@ HISTORY
+(STABLE-1.4.1)
+
+ ++ New features:
+
+ 2012-03-25: Simon Goldschmidt (idea by Mason)
+ * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h
+ which are a simple wrapper to the correct lwIP include files.
+
+ 2012-01-16: Simon Goldschmidt
+ * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP
+
+ 2011-12-17: Simon Goldschmidt
+ * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW)
+ (fixes bug #35061)
+
+ 2011-09-27: Simon Goldschmidt
+ * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989)
+ (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h)
+
+ 2011-09-21: Simon Goldschmidt
+ * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on
+ send (TCP only, bug #33820)
+
+ 2011-09-21: Simon Goldschmidt
+ * init.c: Converted runtime-sanity-checks into compile-time checks that can
+ be disabled (since runtime checks can often not be seen on embedded targets)
+
+ 2011-09-11: Simon Goldschmidt
+ * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file
+ to get a clear separation of which functions an application or port may use
+ (task #11281)
+
+ 2011-09-11: Simon Goldschmidt
+ * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize
+ initial local TCP/UDP ports (so that different port ranges are used after
+ a reboot; bug #33818; this one added tcp_init/udp_init functions again)
+
+ 2011-09-03: Simon Goldschmidt
+ * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302)
+
+ 2011-08-24: Simon Goldschmidt
+ * opt.h, netif.h/.c: added netif remove callback (bug #32397)
+
+ 2011-07-26: Simon Goldschmidt
+ * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter
+ function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN)
+
+ 2011-07-21: Simon Goldschmidt (patch by hanhui)
+ * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour:
+ Added pbuf flags to mark incoming packets as link-layer broadcast/multicast.
+ Also added code to allow ip_forward() to forward non-broadcast packets to
+ the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1).
+
+ 2011-06-26: Simon Goldschmidt (patch by Cameron Gutman)
+ * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that
+ pcb->state != LISTEN
+
+ 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage)
+ * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static
+ memory message
+
+
+ ++ Bugfixes:
+
+ 2012-09-26: Simon Goldschmidt
+ * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object
+
+ 2012-09-26: patch by Henrik Persson
+ * dhcp.c: patch #7843 Fix corner case with dhcp timeouts
+
+ 2012-09-26: patch by Henrik Persson
+ * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet
+
+ 2012-08-22: Simon Goldschmidt
+ * memp.c: fixed bug #37166: memp_sanity check loops itself
+
+ 2012-05-08: Simon Goldschmidt
+ * tcp_out.c: fixed bug: #36380 unsent_oversize mismatch in 1.4.1RC1 (this was
+ a debug-check issue only)
+
+ 2012-03-27: Simon Goldschmidt
+ * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c
+
+ 2012-03-27: Simon Goldschmidt (patch by Mason)
+ * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the
+ send MSS
+
+ 2012-03-22: Simon Goldschmidt
+ * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward
+
+ 2012-03-20: Simon Goldschmidt (patch by Mason)
+ * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list
+
+ 2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic)
+ * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian,
+ possible bug on little endian system
+
+ 2012-02-23: Simon Goldschmidt
+ * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway
+ (introduced when fixing bug# 33551)
+
+ 2012-02-16: Simon Goldschmidt
+ * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP()
+ (bug #35541: PPP Memory Leak)
+
+ 2012-02-16: Simon Goldschmidt
+ * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway
+ (introduced when fixing bug# 33551)
+
+ 2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage)
+ * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed
+
+ 2012-02-15: Simon Goldschmidt
+ * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with
+ MEMP_MEM_MALLOC==1
+
+ 2012-02-12: Simon Goldschmidt
+ * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on
+ MSS > pcb->snd_wnd (by not creating segments bigger than half the window)
+
+ 2012-02-11: Simon Goldschmidt
+ * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait
+ queue while closing
+
+ 2012-01-22: Simon Goldschmidt
+ * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR)
+
+ 2012-01-21: Simon Goldschmidt
+ * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb
+
+ 2012-01-20: Simon Goldschmidt
+ * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths
+
+ 2012-01-20: Simon Goldschmidt
+ * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy
+
+ 2011-11-25: Simon Goldschmidt
+ * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt
+ tcp_active_pcbs in some cases
+
+ 2011-11-23: Simon Goldschmidt
+ * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with
+ '#ifndef sys_msleep'
+
+ 2011-11-22: Simon Goldschmidt
+ * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when
+ netif is brought down
+
+ 2011-10-28: Simon Goldschmidt
+ * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks
+
+ 2011-10-23: Simon Goldschmidt
+ * mem.c: fixed bug #34429: possible memory corruption with
+ LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1
+
+ 2011-10-18: Simon Goldschmidt
+ * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard
+ error value
+
+ 2011-10-18: Simon Goldschmidt
+ * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small
+ windows (bug #34176 select after non-blocking send times out)
+
+ 2011-10-18: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't
+ consider netif->mtu, causes slow network
+
+ 2011-10-18: Simon Goldschmidt
+ * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code
+
+ 2011-10-18: Simon Goldschmidt
+ * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS
+
+ 2011-10-17: Simon Goldschmidt
+ * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api
+
+ 2011-10-13: Simon Goldschmidt
+ * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no
+ zero window is received) by starting the persist timer when a zero window is
+ received, not when we have more data queued for sending than fits into the
+ window
+
+ 2011-10-13: Simon Goldschmidt
+ * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex
+
+ 2011-10-13: Simon Goldschmidt
+ * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is
+ used and not all protocols are enabled
+
+ 2011-10-12: Simon Goldschmidt
+ * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4
+
+ 2011-10-09: Simon Goldschmidt
+ * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect
+ byte value when pcb->unacked != NULL
+
+ 2011-10-09: Simon Goldschmidt
+ * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong
+
+ 2011-09-27: Simon Goldschmidt
+ * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places...
+
+ 2011-09-27: Simon Goldschmidt
+ * tcp_in.c: fixed bug #28288: Data after FIN in oos queue
+
+ 2011-09-27: Simon Goldschmidt
+ * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf
+
+ 2011-09-24: Simon Goldschmidt
+ * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1
+
+ 2011-09-23: Simon Goldschmidt
+ * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for
+ the last packet including FIN can lose data
+
+ 2011-09-22: Simon Goldschmidt
+ * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into
+ account
+
+ 2011-09-21: Simon Goldschmidt
+ * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks
+ in init.c
+
+ 2011-09-20: Simon Goldschmidt
+ * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts)
+
+ 2011-09-11: Simon Goldschmidt
+ * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs
+ (bug #34019)
+
+ 2011-09-09: Simon Goldschmidt
+ * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if
+ udp port matches
+
+ 2011-09-03: Simon Goldschmidt
+ * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet
+ is aggregated and sent to application
+
+ 2011-09-01: Simon Goldschmidt
+ * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared
+ to other options
+
+ 2011-09-01: Simon Goldschmidt
+ * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno
+
+ 2011-08-24: Simon Goldschmidt
+ * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling
+ accept() on UDP connections
+
+ 2011-08-24: Simon Goldschmidt
+ * sockets.h: fixed bug #34057 socklen_t should be a typedef
+
+ 2011-08-24: Simon Goldschmidt
+ * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo)
+
+ 2011-08-24: Simon Goldschmidt
+ * dhcp.c: fixed bug #34122 dhcp: hostname can overflow
+
+ 2011-08-24: Simon Goldschmidt
+ * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr
+
+ 2011-08-22: Simon Goldschmidt
+ * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This
+ merely prevents nagle from not transmitting fast after closing.)
+
+ 2011-07-22: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns
+ always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now
+ lwip_send() sends as much as possible for non-blocking sockets
+
+ 2011-07-22: Simon Goldschmidt
+ * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented
+ for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
+ at regular intervals from main level.
+
+ 2011-07-21: Simon Goldschmidt
+ * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by
+ sending an ARP request when an ARP entry is used in the last minute before
+ it would time out.
+
+ 2011-07-04: Simon Goldschmidt
+ * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0.
+
+ 2011-06-26: Simon Goldschmidt
+ * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by
+ updating its documentation only.
+
+ 2011-06-26: Simon Goldschmidt
+ * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an
+ unaligned pointer.
+
+ 2011-06-26: Simon Goldschmidt
+ * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1"
+
+ 2011-05-25: Simon Goldschmidt
+ * tcp.c: fixed bug #33398 (pointless conversion when checking TCP port range)
+
+
+
(STABLE-1.4.0)
++ New features:
diff --git a/Libraries/Lwip/lwip/COPYING b/Libraries/Lwip/lwip/COPYING
new file mode 100644
index 00000000..e23898b5
--- /dev/null
+++ b/Libraries/Lwip/lwip/COPYING
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
diff --git a/Libraries/Lwip/lwip/UPGRADING b/Libraries/Lwip/lwip/UPGRADING
new file mode 100644
index 00000000..6501107a
--- /dev/null
+++ b/Libraries/Lwip/lwip/UPGRADING
@@ -0,0 +1,144 @@
+This file lists major changes between release versions that require
+ports or applications to be changed. Use it to update a port or an
+application written for an older version of lwIP to correctly work
+with newer versions.
+
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ Application changes:
+
+ * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
+ compatibility to old applications, but will be removed in the future).
+
+ * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
+
+ +++ Raw API:
+ * Changed the semantics of tcp_close() (since it was rather a
+ shutdown before): Now the application does *NOT* get any calls to the recv
+ callback (aside from NULL/closed) after calling tcp_close()
+
+ * When calling tcp_abort() from a raw API TCP callback function,
+ make sure you return ERR_ABRT to prevent accessing unallocated memory.
+ (ERR_ABRT now means the applicaiton has called tcp_abort!)
+
+ +++ Netconn API:
+ * Changed netconn_receive() and netconn_accept() to return
+ err_t, not a pointer to new data/netconn.
+
+ +++ Socket API:
+ * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
+ now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
+
+ * Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ +++ all APIs:
+ * correctly implemented SO(F)_REUSEADDR
+
+ ++ Port changes
+
+ +++ new files:
+
+ * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
+
+ * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
+ the actual application programmer's API
+
+ * Separated timer implementation from sys.h/.c, moved to timers.h/.c;
+ Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
+ still want to use your own timer implementation for NO_SYS==0 (as before).
+
+ +++ sys layer:
+
+ * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
+ sys_sem_t;
+
+ * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+
+ * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
+ binary semaphores instead of mutexes - as before)
+
+ +++ new options:
+
+ * Don't waste memory when chaining segments, added option TCP_OVERSIZE to
+ prevent creating many small pbufs when calling tcp_write with many small
+ blocks of data. Instead, pbufs are allocated larger than needed and the
+ space is used for later calls to tcp_write.
+
+ * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
+ in tcp_write/udp_send.
+
+ * Added an additional option LWIP_ETHERNET to support ethernet without ARP
+ (necessary for pure PPPoE)
+
+ * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
+ be used to place these pools into user-defined memory by using external
+ declaration.
+
+ * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
+
+ +++ new pools:
+
+ * Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
+ so MEMP_NUM_NETDB has to be set accordingly.
+
+ * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
+ MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
+
+ * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
+ to be set accordingly.
+
+ * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
+ has to be set accordingly
+
+ * Integrated loopif into netif.c - loopif does not have to be created by the
+ port any more, just define LWIP_HAVE_LOOPIF to 1.
+
+ * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
+ in cc.h, e.g. used by igmp)
+
+ * Added printf-formatter X8_F to printf u8_t as hex
+
+ * The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+
+ * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
+ with user-allocated structs instead of calling mem_malloc
+
+ * Added const char* name to mem- and memp-stats for easier debugging.
+
+ * Calculate the TCP/UDP checksum while copying to only fetch data once:
+ Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
+
+ * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
+ more than one pcb.
+
+ * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
+ off any more, if this is set to 0, only one packet (the most recent one) is
+ queued (like demanded by RFC 1122).
+
+
+ ++ Major bugfixes/improvements
+
+ * Implemented tcp_shutdown() to only shut down one end of a connection
+ * Implemented shutdown() at socket- and netconn-level
+ * Added errorset support to select() + improved select speed overhead
+ * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
+ * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
+ * Use macros defined in ip_addr.h to work with IP addresses
+ * Implemented many nonblocking socket/netconn functions
+ * Fixed ARP input processing: only add a new entry if a request was directed as us
+ * mem_realloc() to mem_trim() to prevent confusion with realloc()
+ * Some improvements for AutoIP (don't route/forward link-local addresses, don't break
+ existing connections when assigning a routable address)
+ * Correctly handle remote side overrunning our rcv_wnd in ooseq case
+ * Removed packing from ip_addr_t, the packed version is now only used in protocol headers
+ * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
+ * Added support for static ARP table entries
+
+(STABLE-1.3.2)
+
+ * initial version of this file
diff --git a/Libraries/Lwip/lwip/doc/contrib.txt b/Libraries/Lwip/lwip/doc/contrib.txt
index 780268f3..39596fca 100644
--- a/Libraries/Lwip/lwip/doc/contrib.txt
+++ b/Libraries/Lwip/lwip/doc/contrib.txt
@@ -60,4 +60,4 @@ features of Savannah help us not lose users' input.
1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
you think it could benefit others[1] you might want discuss this on the mailing list. You
can also ask for CVS access to submit and maintain your port in the contrib CVS module.
-
+ \ No newline at end of file
diff --git a/Libraries/Lwip/lwip/doc/rawapi.txt b/Libraries/Lwip/lwip/doc/rawapi.txt
index bd452cf9..8c190305 100644
--- a/Libraries/Lwip/lwip/doc/rawapi.txt
+++ b/Libraries/Lwip/lwip/doc/rawapi.txt
@@ -107,7 +107,7 @@ incoming connections or be explicitly connected to another host.
Creates a new connection identifier (PCB). If memory is not
available for creating the new pcb, NULL is returned.
-- err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
+- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
u16_t port)
Binds the pcb to a local IP address and port number. The IP address
@@ -147,6 +147,8 @@ incoming connections or be explicitly connected to another host.
usually be called from the accept callback. This allows lwIP to perform
housekeeping tasks, such as allowing further incoming connections to be
queued in the listen backlog.
+ ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed
+ into the accept callback!
- void tcp_accept(struct tcp_pcb *pcb,
err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
@@ -154,8 +156,8 @@ incoming connections or be explicitly connected to another host.
Specified the callback function that should be called when a new
connection arrives on a listening connection.
-
-- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
+
+- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
u16_t port, err_t (* connected)(void *arg,
struct tcp_pcb *tpcb,
err_t err));
@@ -176,7 +178,7 @@ incoming connections or be explicitly connected to another host.
available for enqueueing the SYN segment. If the SYN indeed was
enqueued successfully, the tcp_connect() function returns ERR_OK.
-
+
--- Sending TCP data
TCP data is sent by enqueueing the data with a call to
@@ -184,15 +186,19 @@ tcp_write(). When the data is successfully transmitted to the remote
host, the application will be notified with a call to a specified
callback function.
-- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len,
- u8_t copy)
+- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+ u8_t apiflags)
Enqueues the data pointed to by the argument dataptr. The length of
- the data is passed as the len parameter. The copy argument is either
- 0 or 1 and indicates whether the new memory should be allocated for
- the data to be copied into. If the argument is 0, no new memory
- should be allocated and the data should only be referenced by
- pointer.
+ the data is passed as the len parameter. The apiflags can be one or more of:
+ - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
+ for the data to be copied into. If this flag is not given, no new memory
+ should be allocated and the data should only be referenced by pointer. This
+ also means that the memory behind dataptr must not change until the data is
+ ACKed by the remote host
+ - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given,
+ the PSH flag is set in the last segment created by this call to tcp_write.
+ If this flag is given, the PSH flag is not set.
The tcp_write() function will fail and return ERR_MEM if the length
of the data exceeds the current send buffer size or if the length of
@@ -238,7 +244,7 @@ window.
Must be called when the application has received the data. The len
argument indicates the length of the received data.
-
+
--- Application polling
@@ -253,7 +259,7 @@ again when the connection has been idle for a while.
- void tcp_poll(struct tcp_pcb *pcb,
err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
- u8_t interval)
+ u8_t interval)
Specifies the polling interval and the callback function that should
be called to poll the application. The interval is specified in
@@ -322,14 +328,14 @@ level of complexity of UDP, the interface is significantly simpler.
Removes and deallocates the pcb.
-- err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr,
+- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr,
u16_t port)
Binds the pcb to a local address. The IP-address argument "ipaddr"
can be IP_ADDR_ANY to indicate that it should listen to any local IP
address. The function currently always return ERR_OK.
-- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr,
+- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr,
u16_t port)
Sets the remote end of the pcb. This function does not generate any
@@ -347,7 +353,7 @@ level of complexity of UDP, the interface is significantly simpler.
- void udp_recv(struct udp_pcb *pcb,
void (* recv)(void *arg, struct udp_pcb *upcb,
struct pbuf *p,
- struct ip_addr *addr,
+ ip_addr_t *addr,
u16_t port),
void *recv_arg)
@@ -408,8 +414,8 @@ Call these functions in the order of appearance:
Note: you must call tcp_fasttmr() and tcp_slowtmr() at the
predefined regular intervals after this initialization.
-- netif_add(struct netif *netif, struct ip_addr *ipaddr,
- struct ip_addr *netmask, struct ip_addr *gw,
+- netif_add(struct netif *netif, ip_addr_t *ipaddr,
+ ip_addr_t *netmask, ip_addr_t *gw,
void *state, err_t (* init)(struct netif *netif),
err_t (* input)(struct pbuf *p, struct netif *netif))
diff --git a/Libraries/Lwip/lwip/doc/savannah.txt b/Libraries/Lwip/lwip/doc/savannah.txt
index e3178997..409905b1 100644
--- a/Libraries/Lwip/lwip/doc/savannah.txt
+++ b/Libraries/Lwip/lwip/doc/savannah.txt
@@ -132,4 +132,4 @@ A new 0.6.3 release is now available here:
http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
You will have to submit this via the user News interface, then approve
-this via the Administrator News interface.
+this via the Administrator News interface. \ No newline at end of file
diff --git a/Libraries/Lwip/lwip/doc/sys_arch.txt b/Libraries/Lwip/lwip/doc/sys_arch.txt
index 38377b66..847cd777 100644
--- a/Libraries/Lwip/lwip/doc/sys_arch.txt
+++ b/Libraries/Lwip/lwip/doc/sys_arch.txt
@@ -34,26 +34,36 @@ in the sys_arch.h file. Mailboxes are equivalently represented by the
type "sys_mbox_t". lwIP does not place any restrictions on how
sys_sem_t or sys_mbox_t are represented internally.
+Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that
+allows both using pointers or actual OS structures to be used. This way, memory
+required for such types can be either allocated in place (globally or on the
+stack) or on the heap (allocated internally in the "*_new()" functions).
+
The following functions must be implemented by the sys_arch:
- void sys_init(void)
Is called to initialize the sys_arch layer.
-- sys_sem_t sys_sem_new(u8_t count)
+- err_t sys_sem_new(sys_sem_t *sem, u8_t count)
- Creates and returns a new semaphore. The "count" argument specifies
- the initial state of the semaphore.
+ Creates a new semaphore. The semaphore is allocated to the memory that 'sem'
+ points to (which can be both a pointer or the actual OS structure).
+ The "count" argument specifies the initial state of the semaphore (which is
+ either 0 or 1).
+ If the semaphore has been created, ERR_OK should be returned. Returning any
+ other error will provide a hint what went wrong, but except for assertions,
+ no real error handling is implemented.
-- void sys_sem_free(sys_sem_t sem)
+- void sys_sem_free(sys_sem_t *sem)
Deallocates a semaphore.
-- void sys_sem_signal(sys_sem_t sem)
+- void sys_sem_signal(sys_sem_t *sem)
Signals a semaphore.
-- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
+- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
Blocks the thread while waiting for the semaphore to be
signaled. If the "timeout" argument is non-zero, the thread should
@@ -70,30 +80,47 @@ The following functions must be implemented by the sys_arch:
Notice that lwIP implements a function with a similar name,
sys_sem_wait(), that uses the sys_arch_sem_wait() function.
-- sys_mbox_t sys_mbox_new(int size)
+- int sys_sem_valid(sys_sem_t *sem)
+
+ Returns 1 if the semaphore is valid, 0 if it is not valid.
+ When using pointers, a simple way is to check the pointer for != NULL.
+ When directly using OS structures, implementing this may be more complex.
+ This may also be a define, in which case the function is not prototyped.
+
+- void sys_sem_set_invalid(sys_sem_t *sem)
+
+ Invalidate a semaphore so that sys_sem_valid() returns 0.
+ ATTENTION: This does NOT mean that the semaphore shall be deallocated:
+ sys_sem_free() is always called before calling this function!
+ This may also be a define, in which case the function is not prototyped.
+
+- err_t sys_mbox_new(sys_mbox_t *mbox, int size)
Creates an empty mailbox for maximum "size" elements. Elements stored
in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
in your lwipopts.h, or ignore this parameter in your implementation
and use a default size.
+ If the mailbox has been created, ERR_OK should be returned. Returning any
+ other error will provide a hint what went wrong, but except for assertions,
+ no real error handling is implemented.
-- void sys_mbox_free(sys_mbox_t mbox)
+- void sys_mbox_free(sys_mbox_t *mbox)
Deallocates a mailbox. If there are messages still present in the
mailbox when the mailbox is deallocated, it is an indication of a
programming error in lwIP and the developer should be notified.
-- void sys_mbox_post(sys_mbox_t mbox, void *msg)
+- void sys_mbox_post(sys_mbox_t *mbox, void *msg)
Posts the "msg" to the mailbox. This function have to block until
the "msg" is really posted.
-- err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg)
+- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
is full, else, ERR_OK if the "msg" is posted.
-- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout)
+- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
Blocks the thread until a message arrives in the mailbox, but does
not block the thread longer than "timeout" milliseconds (similar to
@@ -110,7 +137,7 @@ The following functions must be implemented by the sys_arch:
Note that a function with a similar name, sys_mbox_fetch(), is
implemented by lwIP.
-- u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg)
+- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
This is similar to sys_arch_mbox_fetch, however if a message is not
present in the mailbox, it immediately returns with the code
@@ -122,7 +149,21 @@ The following functions must be implemented by the sys_arch:
#define sys_arch_mbox_tryfetch(mbox,msg) \
sys_arch_mbox_fetch(mbox,msg,1)
although this would introduce unnecessary delays.
-
+
+- int sys_mbox_valid(sys_mbox_t *mbox)
+
+ Returns 1 if the mailbox is valid, 0 if it is not valid.
+ When using pointers, a simple way is to check the pointer for != NULL.
+ When directly using OS structures, implementing this may be more complex.
+ This may also be a define, in which case the function is not prototyped.
+
+- void sys_mbox_set_invalid(sys_mbox_t *mbox)
+
+ Invalidate a mailbox so that sys_mbox_valid() returns 0.
+ ATTENTION: This does NOT mean that the mailbox shall be deallocated:
+ sys_mbox_free() is always called before calling this function!
+ This may also be a define, in which case the function is not prototyped.
+
If threads are supported by the underlying operating system and if
such functionality is needed in lwIP, the following function will have
to be implemented as well:
@@ -156,6 +197,16 @@ to be implemented as well:
more information. This function is only required if your port is supporting
an operating system.
+For some configurations, you also need:
+
+- u32_t sys_now(void)
+
+ This optional function returns the current time in milliseconds (don't care
+ for wraparound, this is only used for time diffs).
+ Not implementing this function means you cannot use some modules (e.g. TCP
+ timestamps, internal timeouts for NO_SYS==1).
+
+
Note:
Be carefull with using mem_malloc() in sys_arch. When malloc() refers to
diff --git a/Libraries/Lwip/lwip/src/api/api_lib.c b/Libraries/Lwip/lwip/src/api/api_lib.c
index b9f373a0..c0f00438 100644
--- a/Libraries/Lwip/lwip/src/api/api_lib.c
+++ b/Libraries/Lwip/lwip/src/api/api_lib.c
@@ -47,7 +47,7 @@
#include "lwip/src/include/lwip/tcpip.h"
#include "lwip/src/include/lwip/memp.h"
-#include "lwip/src/include/lwip/ip.h"
+#include "lwip/src/include/ipv4/lwip/ip.h"
#include "lwip/src/include/lwip/raw.h"
#include "lwip/src/include/lwip/udp.h"
#include "lwip/src/include/lwip/tcp.h"
@@ -372,7 +372,10 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
#endif /* LWIP_SO_RCVTIMEO*/
#if LWIP_TCP
- if (conn->type == NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ if (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
@@ -461,7 +464,10 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
#if LWIP_TCP
- if (conn->type == NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ if (conn->type == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
struct pbuf *p = NULL;
/* This is not a listening netconn, since recvmbox is set */
@@ -485,8 +491,11 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
*new_buf = buf;
/* don't set conn->last_err: it's only ERR_OK, anyway */
return ERR_OK;
- } else
+ }
#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);
@@ -582,31 +591,62 @@ netconn_send(struct netconn *conn, struct netbuf *buf)
* - 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
-netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags)
+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", (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;
+ }
- /* @todo: for non-blocking write, check if 'size' would ever fit into
- snd_queue or snd_buf */
+ /* non-blocking write sends as much */
msg.function = do_write;
msg.msg.conn = conn;
msg.msg.msg.w.dataptr = dataptr;
msg.msg.msg.w.apiflags = apiflags;
msg.msg.msg.w.len = size;
+#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. */
err = TCPIP_APIMSG(&msg);
+ 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;
diff --git a/Libraries/Lwip/lwip/src/api/api_msg.c b/Libraries/Lwip/lwip/src/api/api_msg.c
index fd7d2be7..9fd8be6e 100644
--- a/Libraries/Lwip/lwip/src/api/api_msg.c
+++ b/Libraries/Lwip/lwip/src/api/api_msg.c
@@ -42,14 +42,14 @@
#include "lwip/src/include/lwip/api_msg.h"
-#include "lwip/src/include/lwip/ip.h"
+#include "lwip/src/include/ipv4/lwip/ip.h"
#include "lwip/src/include/lwip/udp.h"
#include "lwip/src/include/lwip/tcp.h"
#include "lwip/src/include/lwip/raw.h"
#include "lwip/src/include/lwip/memp.h"
#include "lwip/src/include/lwip/tcpip.h"
-#include "lwip/src/include/lwip/igmp.h"
+#include "lwip/src/include/ipv4/lwip/igmp.h"
#include "lwip/src/include/lwip/dns.h"
#include <string.h>
@@ -456,6 +456,14 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t 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);
@@ -597,18 +605,16 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
#endif /* LWIP_TCP */
default:
LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
- break;
+ goto free_and_return;
}
#endif
if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
- memp_free(MEMP_NETCONN, conn);
- return NULL;
+ goto free_and_return;
}
if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
sys_sem_free(&conn->op_completed);
- memp_free(MEMP_NETCONN, conn);
- return NULL;
+ goto free_and_return;
}
#if LWIP_TCP
@@ -624,6 +630,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
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 */
@@ -633,6 +642,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
#endif /* LWIP_SO_RCVBUF */
conn->flags = 0;
return conn;
+free_and_return:
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
}
/**
@@ -770,21 +782,21 @@ do_close_internal(struct netconn *conn)
}
}
/* Try to close the connection */
- if (shut == NETCONN_SHUT_RDWR) {
+ if (close) {
err = tcp_close(conn->pcb.tcp);
} else {
- err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR);
+ 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;
- /* Set back some callback pointers as conn is going away */
- conn->pcb.tcp = NULL;
- /* Trigger select() in socket layer. Make sure everybody notices activity
- on the connection, error first! */
if (close) {
+ /* 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) {
@@ -1089,6 +1101,8 @@ do_listen(struct api_msg_msg *msg)
}
}
}
+ } else {
+ msg->err = ERR_ARG;
}
}
}
@@ -1193,7 +1207,7 @@ do_recv(struct api_msg_msg *msg)
static err_t
do_writemore(struct netconn *conn)
{
- err_t err = ERR_OK;
+ err_t err;
void *dataptr;
u16_t len, available;
u8_t write_finished = 0;
@@ -1209,62 +1223,78 @@ do_writemore(struct netconn *conn)
LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
conn->write_offset < conn->current_msg->msg.w.len);
- dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
- diff = conn->current_msg->msg.w.len - conn->write_offset;
- if (diff > 0xffffUL) { /* max_u16_t */
- len = 0xffff;
+#if LWIP_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;
+ 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;
+ 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;
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif
- apiflags |= TCP_WRITE_FLAG_MORE;
- }
- if (dontblock && (len < conn->current_msg->msg.w.len)) {
- /* failed to send all data at once -> nonblocking write not possible */
- err = ERR_MEM;
- }
- if (err == ERR_OK) {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ }
LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
- }
- if (dontblock && (err == ERR_MEM)) {
- /* nonblocking write failed */
- write_finished = 1;
- err = ERR_WOULDBLOCK;
- /* let poll_tcp check writable space to mark the pcb
- writable again */
- conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
- /* let select mark this pcb as non-writable. */
- API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
- } else {
/* if OK or memory error, check available space */
- if (((err == ERR_OK) || (err == ERR_MEM)) &&
- ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
- (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) {
- /* The queued byte- or pbuf-count exceeds the configured low-water limit,
- let select mark this pcb as non-writable. */
- API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ if ((err == ERR_OK) || (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) {
+ 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) {
+ } 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! */
@@ -1272,16 +1302,16 @@ do_writemore(struct netconn *conn)
/* tcp_write returned ERR_MEM, try tcp_output anyway */
tcp_output(conn->pcb.tcp);
- #if LWIP_TCPIP_CORE_LOCKING
+#if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
- #endif
+#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 */
diff --git a/Libraries/Lwip/lwip/src/api/netdb.c b/Libraries/Lwip/lwip/src/api/netdb.c
index 64cb677f..2a00f829 100644
--- a/Libraries/Lwip/lwip/src/api/netdb.c
+++ b/Libraries/Lwip/lwip/src/api/netdb.c
@@ -40,7 +40,7 @@
#include "lwip/src/include/lwip/err.h"
#include "lwip/src/include/lwip/mem.h"
#include "lwip/src/include/lwip/memp.h"
-#include "lwip/src/include/lwip/ip_addr.h"
+#include "lwip/src/include/ipv4/lwip/ip_addr.h"
#include "lwip/src/include/lwip/api.h"
#include "lwip/src/include/lwip/dns.h"
@@ -49,7 +49,7 @@
/** helper struct for gethostbyname_r to access the char* buffer */
struct gethostbyname_r_helper {
- ip_addr_t *addrs;
+ ip_addr_t *addr_list[2];
ip_addr_t addr;
char *aliases;
};
@@ -180,7 +180,7 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
}
/* first thing to do: set *result to nothing */
*result = NULL;
- if ((name == NULL) || (ret == NULL) || (buf == 0)) {
+ if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
/* not all arguments given */
*h_errnop = EINVAL;
return -1;
@@ -197,10 +197,10 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
/* query host IP address */
- err = netconn_gethostbyname(name, &(h->addr));
+ err = netconn_gethostbyname(name, &h->addr);
if (err != ERR_OK) {
LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
- *h_errnop = ENSRNOTFOUND;
+ *h_errnop = HOST_NOT_FOUND;
return -1;
}
@@ -209,13 +209,14 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
hostname[namelen] = 0;
/* fill hostent */
- h->addrs = &(h->addr);
+ h->addr_list[0] = &h->addr;
+ h->addr_list[1] = NULL;
h->aliases = NULL;
- ret->h_name = (char*)hostname;
- ret->h_aliases = &(h->aliases);
+ 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->addrs);
+ ret->h_addr_list = (char**)&h->addr_list;
/* set result != NULL */
*result = ret;
diff --git a/Libraries/Lwip/lwip/src/api/sockets.c b/Libraries/Lwip/lwip/src/api/sockets.c
index 15918d58..676c44bb 100644
--- a/Libraries/Lwip/lwip/src/api/sockets.c
+++ b/Libraries/Lwip/lwip/src/api/sockets.c
@@ -45,7 +45,7 @@
#include "lwip/src/include/lwip/sockets.h"
#include "lwip/src/include/lwip/api.h"
#include "lwip/src/include/lwip/sys.h"
-#include "lwip/src/include/lwip/igmp.h"
+#include "lwip/src/include/ipv4/lwip/igmp.h"
#include "lwip/src/include/lwip/inet.h"
#include "lwip/src/include/lwip/tcp.h"
#include "lwip/src/include/lwip/raw.h"
@@ -336,6 +336,10 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
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 (netconn_type(sock->conn) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
sock_set_errno(sock, err_to_errno(err));
return -1;
}
@@ -537,6 +541,10 @@ lwip_listen(int s, int backlog)
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+ if (netconn_type(sock->conn) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
sock_set_errno(sock, err_to_errno(err));
return -1;
}
@@ -748,6 +756,7 @@ 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));
@@ -766,22 +775,15 @@ lwip_send(int s, const void *data, size_t size, int flags)
#endif /* (LWIP_UDP || LWIP_RAW) */
}
- if ((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) {
- if ((size > TCP_SND_BUF) || ((size / TCP_MSS) > TCP_SND_QUEUELEN)) {
- /* too much data to ever send nonblocking! */
- sock_set_errno(sock, EMSGSIZE);
- return -1;
- }
- }
-
write_flags = NETCONN_COPY |
((flags & MSG_MORE) ? NETCONN_MORE : 0) |
((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
- err = netconn_write(sock->conn, data, size, write_flags);
+ written = 0;
+ err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size));
+ 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)size : -1);
+ return (err == ERR_OK ? (int)written : -1);
}
int
@@ -847,18 +849,29 @@ lwip_sendto(int s, const void *data, size_t size, int flags,
inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr);
remote_port = ntohs(to_in->sin_port);
} else {
- remote_addr = &sock->conn->pcb.raw->remote_ip;
- if (sock->conn->type == NETCONN_RAW) {
- remote_port = 0;
- } else {
+ remote_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 (sock->conn->type == NETCONN_RAW) {
+ if (netconn_type(sock->conn) == NETCONN_RAW) {
+#if LWIP_RAW
err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr);
- } else {
+#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,
@@ -1468,7 +1481,9 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
case SO_ERROR:
case SO_KEEPALIVE:
/* UNIMPL case SO_CONTIMEO: */
- /* UNIMPL case SO_SNDTIMEO: */
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
#endif /* LWIP_SO_RCVTIMEO */
@@ -1676,7 +1691,7 @@ lwip_getsockopt_internal(void *arg)
case SO_REUSEPORT:
#endif /* SO_REUSE */
/*case SO_USELOOPBACK: UNIMPL */
- *(int*)optval = sock->conn->pcb.ip->so_options & optname;
+ *(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;
@@ -1713,6 +1728,11 @@ lwip_getsockopt_internal(void *arg)
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);
@@ -1867,7 +1887,9 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
/* UNIMPL case SO_DONTROUTE: */
case SO_KEEPALIVE:
/* UNIMPL case case SO_CONTIMEO: */
- /* UNIMPL case case SO_SNDTIMEO: */
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
#endif /* LWIP_SO_RCVTIMEO */
@@ -2086,13 +2108,18 @@ lwip_setsockopt_internal(void *arg)
#endif /* SO_REUSE */
/* UNIMPL case SO_USELOOPBACK: */
if (*(int*)optval) {
- sock->conn->pcb.ip->so_options |= optname;
+ ip_set_option(sock->conn->pcb.ip, optname);
} else {
- sock->conn->pcb.ip->so_options &= ~optname;
+ 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);
@@ -2218,7 +2245,7 @@ lwip_setsockopt_internal(void *arg)
case IPPROTO_UDPLITE:
switch (optname) {
case UDPLITE_SEND_CSCOV:
- if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
/* don't allow illegal values! */
sock->conn->pcb.udp->chksum_len_tx = 8;
} else {
@@ -2228,7 +2255,7 @@ lwip_setsockopt_internal(void *arg)
s, (*(int*)optval)) );
break;
case UDPLITE_RECV_CSCOV:
- if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
/* don't allow illegal values! */
sock->conn->pcb.udp->chksum_len_rx = 8;
} else {
diff --git a/Libraries/Lwip/lwip/src/api/tcpip.c b/Libraries/Lwip/lwip/src/api/tcpip.c
index cf82eba4..c3d98163 100644
--- a/Libraries/Lwip/lwip/src/api/tcpip.c
+++ b/Libraries/Lwip/lwip/src/api/tcpip.c
@@ -117,12 +117,6 @@ tcpip_thread(void *arg)
break;
#endif /* LWIP_NETIF_API */
- case TCPIP_MSG_CALLBACK:
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
- msg->msg.cb.function(msg->msg.cb.ctx);
- memp_free(MEMP_TCPIP_MSG_API, msg);
- break;
-
#if LWIP_TCPIP_TIMEOUT
case TCPIP_MSG_TIMEOUT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
@@ -136,6 +130,17 @@ tcpip_thread(void *arg)
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);
@@ -172,22 +177,22 @@ tcpip_input(struct pbuf *p, struct netif *inp)
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
struct tcpip_msg *msg;
- if (sys_mbox_valid(&mbox)) {
- msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
- if (msg == NULL) {
- return ERR_MEM;
- }
+ 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;
+ 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_VAL;
+ return ERR_OK;
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
}
@@ -393,6 +398,52 @@ tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
#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* 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 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
+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
diff --git a/Libraries/Lwip/lwip/src/core/dhcp.c b/Libraries/Lwip/lwip/src/core/dhcp.c
index d4cf0129..efd7dac5 100644
--- a/Libraries/Lwip/lwip/src/core/dhcp.c
+++ b/Libraries/Lwip/lwip/src/core/dhcp.c
@@ -76,7 +76,6 @@
#include "lwip/src/include/ipv4/lwip/ip_addr.h"
#include "lwip/src/include/lwip/netif.h"
#include "lwip/src/include/lwip/def.h"
-#include "lwip/src/include/lwip/sys.h"
#include "lwip/src/include/lwip/dhcp.h"
#include "lwip/src/include/ipv4/lwip/autoip.h"
#include "lwip/src/include/lwip/dns.h"
@@ -84,6 +83,13 @@
#include <string.h>
+/** 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"
@@ -115,7 +121,7 @@
#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_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.
@@ -126,6 +132,11 @@ u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
@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)
@@ -164,6 +175,9 @@ static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
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);
@@ -295,17 +309,7 @@ dhcp_select(struct netif *netif)
dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
#if LWIP_NETIF_HOSTNAME
- if (netif->hostname != NULL) {
- const char *p = (const char*)netif->hostname;
- u8_t namelen = (u8_t)strlen(p);
- if (namelen > 0) {
- LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
- dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
- while (*p) {
- dhcp_option_byte(dhcp, *p++);
- }
- }
- }
+ dhcp_option_hostname(dhcp, netif);
#endif /* LWIP_NETIF_HOSTNAME */
dhcp_option_trailer(dhcp);
@@ -676,7 +680,7 @@ dhcp_start(struct netif *netif)
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
return ERR_MEM;
}
- dhcp->pcb->so_options |= SOF_BROADCAST;
+ 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);
@@ -726,7 +730,7 @@ dhcp_inform(struct netif *netif)
return;
}
dhcp.pcb = pcb;
- dhcp.pcb->so_options |= SOF_BROADCAST;
+ 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"));
}
@@ -958,6 +962,11 @@ dhcp_bind(struct netif *netif)
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);
@@ -1024,20 +1033,6 @@ dhcp_renew(struct netif *netif)
dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-#if LWIP_NETIF_HOSTNAME
- if (netif->hostname != NULL) {
- const char *p = (const char*)netif->hostname;
- u8_t namelen = (u8_t)strlen(p);
- if (namelen > 0) {
- LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
- dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
- while (*p) {
- dhcp_option_byte(dhcp, *p++);
- }
- }
- }
-#endif /* LWIP_NETIF_HOSTNAME */
-
#if 0
dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
@@ -1047,6 +1042,11 @@ dhcp_renew(struct netif *netif)
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);
@@ -1088,17 +1088,7 @@ dhcp_rebind(struct netif *netif)
dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
#if LWIP_NETIF_HOSTNAME
- if (netif->hostname != NULL) {
- const char *p = (const char*)netif->hostname;
- u8_t namelen = (u8_t)strlen(p);
- if (namelen > 0) {
- LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
- dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
- while (*p) {
- dhcp_option_byte(dhcp, *p++);
- }
- }
- }
+ dhcp_option_hostname(dhcp, netif);
#endif /* LWIP_NETIF_HOSTNAME */
#if 0
@@ -1310,6 +1300,29 @@ dhcp_option_long(struct dhcp *dhcp, u32_t value)
dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
}
+#if LWIP_NETIF_HOSTNAME
+static void
+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.
*
@@ -1387,44 +1400,44 @@ again:
offset--;
break;
case(DHCP_OPTION_SUBNET_MASK):
- LWIP_ASSERT("len == 4", len == 4);
+ 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_ASSERT("len >= decode_len", len >= decode_len);
+ 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_ASSERT("len % 4 == 0", len % 4 == 0);
+ 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_ASSERT("len >= decode_len", len >= decode_len);
+ 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_ASSERT("len == 4", len == 4);
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
break;
case(DHCP_OPTION_OVERLOAD):
- LWIP_ASSERT("len == 1", len == 1);
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_OVERLOAD;
break;
case(DHCP_OPTION_MESSAGE_TYPE):
- LWIP_ASSERT("len == 1", len == 1);
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
break;
case(DHCP_OPTION_SERVER_ID):
- LWIP_ASSERT("len == 4", len == 4);
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_SERVER_ID;
break;
case(DHCP_OPTION_T1):
- LWIP_ASSERT("len == 4", len == 4);
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_T1;
break;
case(DHCP_OPTION_T2):
- LWIP_ASSERT("len == 4", len == 4);
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_T2;
break;
default:
@@ -1438,32 +1451,39 @@ again:
u16_t copy_len;
decode_next:
LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
- LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx));
- copy_len = LWIP_MIN(decode_len, 4);
- pbuf_copy_partial(q, &value, copy_len, val_offset);
- if (decode_len > 4) {
- /* decode more than one u32_t */
- LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0);
+ 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, htonl(value));
- decode_len -= 4;
- val_offset += 4;
- decode_idx++;
- goto decode_next;
- } else if (decode_len == 4) {
- value = ntohl(value);
- } else {
- LWIP_ASSERT("invalid decode_len", decode_len == 1);
- value = ((u8_t*)&value)[0];
+ dhcp_set_option_value(dhcp, decode_idx, value);
}
- dhcp_got_option(dhcp, decode_idx);
- dhcp_set_option_value(dhcp, decode_idx, value);
}
if (offset >= q->len) {
offset -= q->len;
offset_max -= q->len;
- q = q->next;
- options = (u8_t*)q->payload;
+ 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? */
@@ -1626,10 +1646,12 @@ dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
* 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
- static u32_t xid;
- static u8_t xid_initialised = 0;
if (!xid_initialised) {
xid = DHCP_GLOBAL_XID;
xid_initialised = !xid_initialised;
@@ -1650,7 +1672,11 @@ dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
/* reuse transaction identifier in retransmissions */
if (dhcp->tries == 0) {
- xid++;
+#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,
@@ -1734,9 +1760,8 @@ dhcp_option_trailer(struct dhcp *dhcp)
LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
/* packet is too small, or not 4 byte aligned? */
- while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) {
- /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */
- LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ 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;
}
diff --git a/Libraries/Lwip/lwip/src/core/ipv4/autoip.c b/Libraries/Lwip/lwip/src/core/ipv4/autoip.c
index bac735e9..64ef6b52 100644
--- a/Libraries/Lwip/lwip/src/core/ipv4/autoip.c
+++ b/Libraries/Lwip/lwip/src/core/ipv4/autoip.c
@@ -68,9 +68,9 @@
#include "lwip/src/include/lwip/mem.h"
#include "lwip/src/include/lwip/udp.h"
-#include "lwip/src/include/lwip/ip_addr.h"
+#include "lwip/src/include/ipv4/lwip/ip_addr.h"
#include "lwip/src/include/lwip/netif.h"
-#include "lwip/src/include/lwip/autoip.h"
+#include "lwip/src/include/ipv4/lwip/autoip.h"
#include "lwip/src/include/netif/etharp.h"
#include <stdlib.h>
@@ -122,14 +122,6 @@ static err_t autoip_bind(struct netif *netif);
/* start sending probes for llipaddr */
static void autoip_start_probing(struct netif *netif);
-/**
- * Initialize this module
- */
-void
-autoip_init(void)
-{
- LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n"));
-}
/** Set a statically allocated struct autoip to work with.
* Using this prevents autoip_start to allocate it using mem_malloc.
@@ -170,8 +162,8 @@ 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) {
+ if (defend) {
+ if (netif->autoip->lastconflict > 0) {
/* retreat, there was a conflicting ARP in the last
* DEFEND_INTERVAL seconds
*/
@@ -295,7 +287,7 @@ autoip_start(struct netif *netif)
struct autoip *autoip = netif->autoip;
err_t result = ERR_OK;
- if(netif_is_up(netif)) {
+ if (netif_is_up(netif)) {
netif_set_down(netif);
}
@@ -309,12 +301,12 @@ autoip_start(struct netif *netif)
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) {
+ 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) {
+ if (autoip == NULL) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_start(): could not allocate autoip\n"));
return ERR_MEM;
@@ -360,7 +352,7 @@ autoip_start_probing(struct netif *netif)
* accquiring and probing address
* compliant to RFC 3927 Section 2.2.1
*/
- if(autoip->tried_llipaddr > MAX_CONFLICTS) {
+ if (autoip->tried_llipaddr > MAX_CONFLICTS) {
autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
}
}
@@ -404,7 +396,7 @@ autoip_tmr()
while (netif != NULL) {
/* only act on AutoIP configured interfaces */
if (netif->autoip != NULL) {
- if(netif->autoip->lastconflict > 0) {
+ if (netif->autoip->lastconflict > 0) {
netif->autoip->lastconflict--;
}
@@ -414,10 +406,10 @@ autoip_tmr()
switch(netif->autoip->state) {
case AUTOIP_STATE_PROBING:
- if(netif->autoip->ttw > 0) {
+ if (netif->autoip->ttw > 0) {
netif->autoip->ttw--;
} else {
- if(netif->autoip->sent_num >= PROBE_NUM) {
+ 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;
@@ -439,10 +431,10 @@ autoip_tmr()
break;
case AUTOIP_STATE_ANNOUNCING:
- if(netif->autoip->ttw > 0) {
+ if (netif->autoip->ttw > 0) {
netif->autoip->ttw--;
} else {
- if(netif->autoip->sent_num == 0) {
+ 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.
*
@@ -458,7 +450,7 @@ autoip_tmr()
netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
netif->autoip->sent_num++;
- if(netif->autoip->sent_num >= ANNOUNCE_NUM) {
+ if (netif->autoip->sent_num >= ANNOUNCE_NUM) {
netif->autoip->state = AUTOIP_STATE_BOUND;
netif->autoip->sent_num = 0;
netif->autoip->ttw = 0;
diff --git a/Libraries/Lwip/lwip/src/core/ipv4/icmp.c b/Libraries/Lwip/lwip/src/core/ipv4/icmp.c
index 1011c809..57a22d56 100644
--- a/Libraries/Lwip/lwip/src/core/ipv4/icmp.c
+++ b/Libraries/Lwip/lwip/src/core/ipv4/icmp.c
@@ -190,12 +190,16 @@ icmp_input(struct pbuf *p, struct netif *inp)
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);
diff --git a/Libraries/Lwip/lwip/src/core/ipv4/igmp.c b/Libraries/Lwip/lwip/src/core/ipv4/igmp.c
index f1ee4404..88041996 100644
--- a/Libraries/Lwip/lwip/src/core/ipv4/igmp.c
+++ b/Libraries/Lwip/lwip/src/core/ipv4/igmp.c
@@ -81,14 +81,14 @@ Steve Reynolds
#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
-#include "lwip/src/include/lwip/igmp.h"
+#include "lwip/src/include/ipv4/lwip/igmp.h"
#include "lwip/src/include/lwip/debug.h"
#include "lwip/src/include/lwip/def.h"
#include "lwip/src/include/lwip/mem.h"
-#include "lwip/src/include/lwip/ip.h"
-#include "lwip/src/include/lwip/inet_chksum.h"
+#include "lwip/src/include/ipv4/lwip/ip.h"
+#include "lwip/src/include/ipv4/lwip/inet_chksum.h"
#include "lwip/src/include/lwip/netif.h"
-#include "lwip/src/include/lwip/icmp.h"
+#include "lwip/src/include/ipv4/lwip/icmp.h"
#include "lwip/src/include/lwip/udp.h"
#include "lwip/src/include/lwip/tcp.h"
#include "lwip/src/include/lwip/stats.h"
@@ -139,7 +139,6 @@ static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
static err_t igmp_remove_group(struct igmp_group *group);
static void igmp_timeout( struct igmp_group *group);
static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
-static void igmp_stop_timer(struct igmp_group *group);
static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
static void igmp_send(struct igmp_group *group, u8_t type);
@@ -707,17 +706,6 @@ igmp_start_timer(struct igmp_group *group, u8_t max_time)
}
/**
- * Stop a timer for an igmp_group
- *
- * @param group the igmp_group for which to stop the timer
- */
-static void
-igmp_stop_timer(struct igmp_group *group)
-{
- group->timer = 0;
-}
-
-/**
* Delaying membership report for a group if necessary
*
* @param group the igmp_group for which "delaying" membership report
diff --git a/Libraries/Lwip/lwip/src/core/ipv4/ip.c b/Libraries/Lwip/lwip/src/core/ipv4/ip.c
index 99422e6f..1efa1f32 100644
--- a/Libraries/Lwip/lwip/src/core/ipv4/ip.c
+++ b/Libraries/Lwip/lwip/src/core/ipv4/ip.c
@@ -54,7 +54,7 @@
#include "lwip/src/include/lwip/dhcp.h"
#include "lwip/src/include/ipv4/lwip/autoip.h"
#include "lwip/src/include/lwip/stats.h"
-#include "lwip/src/sam/include/arch/perf.h"
+#include "arch/perf.h"
#include <string.h>
@@ -83,7 +83,7 @@
|| (LWIP_IP_ACCEPT_UDP_PORT(port)))
#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
/* accept custom port only */
-#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port))
+#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))
@@ -125,8 +125,15 @@ 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) {
+ 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))) {
@@ -148,6 +155,41 @@ ip_route(ip_addr_t *dest)
#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
+ip_canforward(struct pbuf *p)
+{
+ u32_t addr = 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 == (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.
@@ -163,6 +205,10 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
PERF_START;
+ if (!ip_canforward(p)) {
+ goto return_noroute;
+ }
+
/* RFC3927 2.7: do not forward link-local addresses */
if (ip_addr_islinklocal(&current_iphdr_dest)) {
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
@@ -177,14 +223,17 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
+ /* @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);
@@ -216,6 +265,20 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
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, &current_iphdr_dest);
return;
@@ -264,6 +327,13 @@ ip_input(struct pbuf *p, struct netif *inp)
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 */
@@ -488,7 +558,6 @@ ip_input(struct pbuf *p, struct netif *inp)
if (raw_input(p, inp) == 0)
#endif /* LWIP_RAW */
{
-
switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
case IP_PROTO_UDP:
@@ -657,9 +726,10 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
#endif /* CHECKSUM_GEN_IP_INLINE */
- IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos);
+ IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
+ IPH_TOS_SET(iphdr, tos);
#if CHECKSUM_GEN_IP_INLINE
- chk_sum += iphdr->_v_hl_tos;
+ 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
@@ -801,9 +871,9 @@ ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
return ERR_RTE;
}
- netif->addr_hint = addr_hint;
+ NETIF_SET_HWADDRHINT(netif, addr_hint);
err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
- netif->addr_hint = NULL;
+ NETIF_SET_HWADDRHINT(netif, NULL);
return err;
}
@@ -817,10 +887,6 @@ void
ip_debug_print(struct pbuf *p)
{
struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
- u8_t *payload;
-
- payload = (u8_t *)iphdr + IP_HLEN;
- (void)payload; // suppress compiler warning
LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
diff --git a/Libraries/Lwip/lwip/src/core/ipv6/README b/Libraries/Lwip/lwip/src/core/ipv6/README
new file mode 100644
index 00000000..36200048
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/ipv6/README
@@ -0,0 +1 @@
+IPv6 support in lwIP is very experimental.
diff --git a/Libraries/Lwip/lwip/src/core/ipv6/icmp6.c b/Libraries/Lwip/lwip/src/core/ipv6/icmp6.c
new file mode 100644
index 00000000..4fcc8955
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/ipv6/icmp6.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+ struct icmp_echo_hdr *iecho;
+ struct ip_hdr *iphdr;
+ struct ip_addr tmpaddr;
+
+ ICMP_STATS_INC(icmp.recv);
+
+ /* TODO: check length before accessing payload! */
+
+ type = ((u8_t *)p->payload)[0];
+
+ switch (type) {
+ case ICMP6_ECHO:
+ 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"));
+
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ return;
+ }
+ iecho = p->payload;
+ iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN);
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
+ ICMP_STATS_INC(icmp.chkerr);
+ /* return;*/
+ }
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len));
+ ip_addr_set(&tmpaddr, &(iphdr->src));
+ ip_addr_set(&(iphdr->src), &(iphdr->dest));
+ ip_addr_set(&(iphdr->dest), &tmpaddr);
+ iecho->type = ICMP6_ER;
+ /* adjust the checksum */
+ if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) {
+ iecho->chksum += htons(ICMP6_ECHO << 8) + 1;
+ } else {
+ iecho->chksum += htons(ICMP6_ECHO << 8);
+ }
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
+ ICMP_STATS_INC(icmp.xmit);
+
+ /* LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/
+ ip_output_if (p, &(iphdr->src), IP_HDRINCL,
+ iphdr->hoplim, IP_PROTO_ICMP, inp);
+ break;
+ default:
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+
+ pbuf_free(p);
+}
+
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ struct icmp_dur_hdr *idur;
+
+ /* @todo: can this be PBUF_LINK instead of PBUF_IP? */
+ q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
+ /* ICMP header + IP header + 8 bytes of data */
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n"));
+ pbuf_free(p);
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (8 + IP_HLEN + 8)));
+
+ iphdr = p->payload;
+
+ idur = q->payload;
+ idur->type = (u8_t)ICMP6_DUR;
+ idur->icode = (u8_t)t;
+
+ SMEMCPY((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8);
+
+ /* calculate checksum */
+ idur->chksum = 0;
+ idur->chksum = inet_chksum(idur, q->len);
+ ICMP_STATS_INC(icmp.xmit);
+
+ ip_output(q, NULL,
+ (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
+ pbuf_free(q);
+}
+
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ struct icmp_te_hdr *tehdr;
+
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n"));
+
+ /* @todo: can this be PBUF_LINK instead of PBUF_IP? */
+ q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
+ /* ICMP header + IP header + 8 bytes of data */
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n"));
+ pbuf_free(p);
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (8 + IP_HLEN + 8)));
+
+ iphdr = p->payload;
+
+ tehdr = q->payload;
+ tehdr->type = (u8_t)ICMP6_TE;
+ tehdr->icode = (u8_t)t;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8);
+
+ /* calculate checksum */
+ tehdr->chksum = 0;
+ tehdr->chksum = inet_chksum(tehdr, q->len);
+ ICMP_STATS_INC(icmp.xmit);
+ ip_output(q, NULL,
+ (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP */
diff --git a/Libraries/Lwip/lwip/src/core/ipv6/inet6.c b/Libraries/Lwip/lwip/src/core/ipv6/inet6.c
new file mode 100644
index 00000000..c3de85c0
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/ipv6/inet6.c
@@ -0,0 +1,163 @@
+/**
+ * @file
+ * Functions common to all TCP/IPv6 modules, such as the Internet checksum and the
+ * byte order functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/inet.h"
+
+/* chksum:
+ *
+ * Sums up all 16 bit words in a memory portion. Also includes any odd byte.
+ * This function is used by the other checksum functions.
+ *
+ * For now, this is not optimized. Must be optimized for the particular processor
+ * arcitecture on which it is to run. Preferebly coded in assembler.
+ */
+
+static u32_t
+chksum(void *dataptr, u16_t len)
+{
+ u16_t *sdataptr = dataptr;
+ u32_t acc;
+
+
+ for(acc = 0; len > 1; len -= 2) {
+ acc += *sdataptr++;
+ }
+
+ /* add up any odd byte */
+ if (len == 1) {
+ acc += htons((u16_t)(*(u8_t *)dataptr) << 8);
+ }
+
+ return acc;
+
+}
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ */
+
+u16_t
+inet_chksum_pseudo(struct pbuf *p,
+ struct ip_addr *src, struct ip_addr *dest,
+ u8_t proto, u32_t proto_len)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped, i;
+
+ acc = 0;
+ swapped = 0;
+ for(q = p; q != NULL; q = q->next) {
+ acc += chksum(q->payload, q->len);
+ while (acc >> 16) {
+ acc = (acc & 0xffff) + (acc >> 16);
+ }
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
+ }
+ }
+
+ if (swapped) {
+ acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
+ }
+
+ for(i = 0; i < 8; i++) {
+ acc += ((u16_t *)src->addr)[i] & 0xffff;
+ acc += ((u16_t *)dest->addr)[i] & 0xffff;
+ while (acc >> 16) {
+ acc = (acc & 0xffff) + (acc >> 16);
+ }
+ }
+ acc += (u16_t)htons((u16_t)proto);
+ acc += ((u16_t *)&proto_len)[0] & 0xffff;
+ acc += ((u16_t *)&proto_len)[1] & 0xffff;
+
+ while (acc >> 16) {
+ acc = (acc & 0xffff) + (acc >> 16);
+ }
+ return ~(acc & 0xffff);
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarely for IP
+ * and ICMP.
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+ u32_t acc, sum;
+
+ acc = chksum(dataptr, len);
+ sum = (acc & 0xffff) + (acc >> 16);
+ sum += (sum >> 16);
+ return ~(sum & 0xffff);
+}
+
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ for(q = p; q != NULL; q = q->next) {
+ acc += chksum(q->payload, q->len);
+ while (acc >> 16) {
+ acc = (acc & 0xffff) + (acc >> 16);
+ }
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8);
+ }
+ }
+
+ if (swapped) {
+ acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
+ }
+ return ~(acc & 0xffff);
+}
diff --git a/Libraries/Lwip/lwip/src/core/ipv6/ip6.c b/Libraries/Lwip/lwip/src/core/ipv6/ip6.c
new file mode 100644
index 00000000..ad59b725
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/ipv6/ip6.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
+
+/* ip.c
+ *
+ * This is the code for the IP layer for IPv6.
+ *
+ */
+
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+
+#include "lwip/stats.h"
+
+#include "arch/perf.h"
+
+/* ip_init:
+ *
+ * Initializes the IP layer.
+ */
+
+void
+ip_init(void)
+{
+}
+
+/* ip_route:
+ *
+ * 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.
+ */
+
+struct netif *
+ip_route(struct ip_addr *dest)
+{
+ struct netif *netif;
+
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ return netif;
+ }
+ }
+
+ return netif_default;
+}
+
+/* ip_forward:
+ *
+ * Forwards an IP packet. It finds an appropriate route for the packet, decrements
+ * the TTL value of the packet, adjusts the checksum and outputs the packet on the
+ * appropriate interface.
+ */
+
+static void
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr)
+{
+ struct netif *netif;
+
+ PERF_START;
+
+ if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) {
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for "));
+#if IP_DEBUG
+ ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest)));
+#endif /* IP_DEBUG */
+ LWIP_DEBUGF(IP_DEBUG, ("\n"));
+ pbuf_free(p);
+ return;
+ }
+ /* Decrement TTL and send ICMP if ttl == 0. */
+ if (--iphdr->hoplim == 0) {
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (iphdr->nexthdr != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Incremental update of the IP checksum. */
+ /* if (iphdr->chksum >= htons(0xffff - 0x100)) {
+ iphdr->chksum += htons(0x100) + 1;
+ } else {
+ iphdr->chksum += htons(0x100);
+ }*/
+
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to "));
+#if IP_DEBUG
+ ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest)));
+#endif /* IP_DEBUG */
+ LWIP_DEBUGF(IP_DEBUG, ("\n"));
+
+ IP_STATS_INC(ip.fw);
+ IP_STATS_INC(ip.xmit);
+
+ PERF_STOP("ip_forward");
+
+ netif->output(netif, p, (struct ip_addr *)&(iphdr->dest));
+}
+
+/* ip_input:
+ *
+ * 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.
+ */
+
+void
+ip_input(struct pbuf *p, struct netif *inp) {
+ struct ip_hdr *iphdr;
+ struct netif *netif;
+
+
+ PERF_START;
+
+#if IP_DEBUG
+ ip_debug_print(p);
+#endif /* IP_DEBUG */
+
+
+ IP_STATS_INC(ip.recv);
+
+ /* identify the IP header */
+ iphdr = p->payload;
+
+
+ if (iphdr->v != 6) {
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n"));
+#if IP_DEBUG
+ ip_debug_print(p);
+#endif /* IP_DEBUG */
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ return;
+ }
+
+ /* is this packet for us? */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+#if IP_DEBUG
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest "));
+ ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr "));
+ ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("\n"));
+#endif /* IP_DEBUG */
+ if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) {
+ break;
+ }
+ }
+
+
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+#if IP_FORWARD
+ ip_forward(p, iphdr);
+#endif
+ pbuf_free(p);
+ return;
+ }
+
+ pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len));
+
+ /* send to upper layers */
+#if IP_DEBUG
+ /* LWIP_DEBUGF("ip_input: \n");
+ ip_debug_print(p);
+ LWIP_DEBUGF("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/
+#endif /* IP_DEBUG */
+
+ if(pbuf_header(p, -IP_HLEN)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ return;
+ }
+
+ switch (iphdr->nexthdr) {
+ case IP_PROTO_UDP:
+ udp_input(p, inp);
+ break;
+ case IP_PROTO_TCP:
+ tcp_input(p, inp);
+ break;
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+ default:
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable */
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n",
+ iphdr->nexthdr));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ }
+ PERF_STOP("ip_input");
+}
+
+
+/* ip_output_if:
+ *
+ * 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.
+ */
+
+err_t
+ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+ u8_t ttl,
+ u8_t proto, struct netif *netif)
+{
+ struct ip_hdr *iphdr;
+
+ PERF_START;
+
+ LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len));
+ if (pbuf_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n"));
+ IP_STATS_INC(ip.err);
+
+ return ERR_BUF;
+ }
+ LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ iphdr = p->payload;
+
+
+ if (dest != IP_HDRINCL) {
+ LWIP_DEBUGF(IP_DEBUG, ("!IP_HDRLINCL\n"));
+ iphdr->hoplim = ttl;
+ iphdr->nexthdr = proto;
+ iphdr->len = htons(p->tot_len - IP_HLEN);
+ ip_addr_set(&(iphdr->dest), dest);
+
+ iphdr->v = 6;
+
+ if (ip_addr_isany(src)) {
+ ip_addr_set(&(iphdr->src), &(netif->ip_addr));
+ } else {
+ ip_addr_set(&(iphdr->src), src);
+ }
+
+ } else {
+ dest = &(iphdr->dest);
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len));
+#if IP_DEBUG
+ ip_debug_print(p);
+#endif /* IP_DEBUG */
+
+ PERF_STOP("ip_output_if");
+ return netif->output(netif, p, dest);
+}
+
+/* ip_output:
+ *
+ * Simple interface to ip_output_if. It finds the outgoing network interface and
+ * calls upon ip_output_if to do the actual work.
+ */
+
+err_t
+ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+ u8_t ttl, u8_t proto)
+{
+ struct netif *netif;
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip_output_if (p, src, dest, ttl, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+err_t
+ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ LWIP_NETIF_HWADDRHINT(netif, addr_hint);
+ err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
+ LWIP_NETIF_HWADDRHINT(netif, NULL);
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+void
+ip_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = p->payload;
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" | %"X16_F"%"X16_F" | %"X16_F"%"X16_F" | (v, traffic class, flow label)\n",
+ iphdr->v,
+ iphdr->tclass1, iphdr->tclass2,
+ iphdr->flow1, iphdr->flow2));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" | %2"U16_F" | %2"U16_F" | (len, nexthdr, hoplim)\n",
+ ntohs(iphdr->len),
+ iphdr->nexthdr,
+ iphdr->hoplim));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n",
+ (ntohl(iphdr->src.addr[0]) >> 16) & 0xffff,
+ ntohl(iphdr->src.addr[0]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n",
+ (ntohl(iphdr->src.addr[1]) >> 16) & 0xffff,
+ ntohl(iphdr->src.addr[1]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n",
+ (ntohl(iphdr->src.addr[2]) >> 16) & 0xffff,
+ ntohl(iphdr->src.addr[2]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n",
+ (ntohl(iphdr->src.addr[3]) >> 16) & 0xffff,
+ ntohl(iphdr->src.addr[3]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n",
+ (ntohl(iphdr->dest.addr[0]) >> 16) & 0xffff,
+ ntohl(iphdr->dest.addr[0]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n",
+ (ntohl(iphdr->dest.addr[1]) >> 16) & 0xffff,
+ ntohl(iphdr->dest.addr[1]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n",
+ (ntohl(iphdr->dest.addr[2]) >> 16) & 0xffff,
+ ntohl(iphdr->dest.addr[2]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n",
+ (ntohl(iphdr->dest.addr[3]) >> 16) & 0xffff,
+ ntohl(iphdr->dest.addr[3]) & 0xffff));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
diff --git a/Libraries/Lwip/lwip/src/core/ipv6/ip6_addr.c b/Libraries/Lwip/lwip/src/core/ipv6/ip6_addr.c
new file mode 100644
index 00000000..2da6cea4
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/ipv6/ip6_addr.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/inet.h"
+
+u8_t
+ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2,
+ struct ip_addr *mask)
+{
+ return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) &&
+ (addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) &&
+ (addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) &&
+ (addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3]));
+
+}
+
+u8_t
+ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2)
+{
+ return(addr1->addr[0] == addr2->addr[0] &&
+ addr1->addr[1] == addr2->addr[1] &&
+ addr1->addr[2] == addr2->addr[2] &&
+ addr1->addr[3] == addr2->addr[3]);
+}
+
+void
+ip_addr_set(struct ip_addr *dest, struct ip_addr *src)
+{
+ SMEMCPY(dest, src, sizeof(struct ip_addr));
+ /* dest->addr[0] = src->addr[0];
+ dest->addr[1] = src->addr[1];
+ dest->addr[2] = src->addr[2];
+ dest->addr[3] = src->addr[3];*/
+}
+
+u8_t
+ip_addr_isany(struct ip_addr *addr)
+{
+ if (addr == NULL) return 1;
+ return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0);
+}
diff --git a/Libraries/Lwip/lwip/src/core/init.c b/Libraries/Lwip/lwip/src/core/lwip_init.c
index f9a86fb8..f9a86fb8 100644
--- a/Libraries/Lwip/lwip/src/core/init.c
+++ b/Libraries/Lwip/lwip/src/core/lwip_init.c
diff --git a/Libraries/Lwip/lwip/src/core/lwip_timers_140.c b/Libraries/Lwip/lwip/src/core/lwip_timers_141.c
index 12a3f6a3..d7cdbf5b 100644
--- a/Libraries/Lwip/lwip/src/core/lwip_timers_140.c
+++ b/Libraries/Lwip/lwip/src/core/lwip_timers_141.c
@@ -57,6 +57,10 @@
#include "lwip/src/include/ipv4/lwip/igmp.h"
#include "lwip/src/include/lwip/dns.h"
+/** Fix lwIP warning in standalone mode */
+#if NO_SYS
+#include "lwip/src/include/lwip/sys.h"
+#endif
/** The one and only timeout list */
static struct sys_timeo *next_timeout;
@@ -365,7 +369,7 @@ sys_check_timeouts(void)
now = sys_now();
if (next_timeout) {
/* this cares for wraparounds */
- diff = LWIP_U32_DIFF(now, timeouts_last_time);
+ diff = now - timeouts_last_time;//LWIP_U32_DIFF(now, timeouts_last_time);
do
{
had_one = 0;
diff --git a/Libraries/Lwip/lwip/src/core/mem.c b/Libraries/Lwip/lwip/src/core/mem.c
index 64b4a536..e6281f13 100644
--- a/Libraries/Lwip/lwip/src/core/mem.c
+++ b/Libraries/Lwip/lwip/src/core/mem.c
@@ -78,9 +78,10 @@
void *
mem_malloc(mem_size_t size)
{
+ void *ret;
struct memp_malloc_helper *element;
memp_t poolnr;
- mem_size_t required_size = size + sizeof(struct memp_malloc_helper);
+ 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
@@ -113,9 +114,9 @@ again:
/* save the pool number this element came from */
element->poolnr = poolnr;
/* and return a pointer to the memory directly after the struct memp_malloc_helper */
- element++;
+ ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
- return element;
+ return ret;
}
/**
@@ -128,13 +129,13 @@ again:
void
mem_free(void *rmem)
{
- struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)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--;
+ 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)));
@@ -189,8 +190,8 @@ static struct mem *ram_end;
/** pointer to the lowest free block, this is used for faster search */
static struct mem *lfree;
-#if 0 // unused in Duet firmware
/** concurrent access protection */
+#if !NO_SYS
static sys_mutex_t mem_mutex;
#endif
@@ -520,7 +521,7 @@ mem_malloc(mem_size_t size)
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 */
+ /* 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 */
@@ -534,12 +535,14 @@ mem_malloc(mem_size_t size)
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
mem_free_count = 0;
LWIP_MEM_ALLOC_UNPROTECT();
- /* allow mem_free to run */
+ /* allow mem_free or mem_trim to run */
LWIP_MEM_ALLOC_PROTECT();
if (mem_free_count != 0) {
- local_mem_free_count = mem_free_count;
+ /* 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;
}
- mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
if ((!mem->used) &&
@@ -583,15 +586,27 @@ mem_malloc(mem_size_t size)
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 (lfree->used && lfree != ram_end) {
+ 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();
- lfree = (struct mem *)(void *)&ram[lfree->next];
+ 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();
diff --git a/Libraries/Lwip/lwip/src/core/memp.c b/Libraries/Lwip/lwip/src/core/memp.c
index 6377e2a8..4136b3ef 100644
--- a/Libraries/Lwip/lwip/src/core/memp.c
+++ b/Libraries/Lwip/lwip/src/core/memp.c
@@ -176,19 +176,20 @@ static u8_t memp_memory[MEM_ALIGNMENT - 1
#if MEMP_SANITY_CHECK
/**
- * Check that memp-lists don't form a circle
+ * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
*/
static int
memp_sanity(void)
{
- s16_t i, c;
- struct memp *m, *n;
+ s16_t i;
+ struct memp *t, *h;
for (i = 0; i < MEMP_MAX; i++) {
- for (m = memp_tab[i]; m != NULL; m = m->next) {
- c = 1;
- for (n = memp_tab[i]; n != NULL; n = n->next) {
- if (n == m && --c < 0) {
+ 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;
}
}
diff --git a/Libraries/Lwip/lwip/src/core/netif.c b/Libraries/Lwip/lwip/src/core/netif.c
index 8aa036ca..a197cdb9 100644
--- a/Libraries/Lwip/lwip/src/core/netif.c
+++ b/Libraries/Lwip/lwip/src/core/netif.c
@@ -54,7 +54,7 @@
#endif /* ENABLE_LOOPBACK */
#if LWIP_AUTOIP
-#include "lwip/src/include/lwip/autoip.h"
+#include "lwip/src/include/ipv4/lwip/autoip.h"
#endif /* LWIP_AUTOIP */
#if LWIP_DHCP
#include "lwip/src/include/lwip/dhcp.h"
@@ -75,6 +75,8 @@
struct netif *netif_list;
struct netif *netif_default;
+static u8_t netif_num;
+
#if LWIP_HAVE_LOOPIF
static struct netif loop_netif;
@@ -100,7 +102,8 @@ netif_loopif_init(struct netif *netif)
}
#endif /* LWIP_HAVE_LOOPIF */
-void netif_init(void)
+void
+netif_init(void)
{
#if LWIP_HAVE_LOOPIF
ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
@@ -136,9 +139,8 @@ struct netif *
netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
{
- static u8_t netifnum = 0;
-/** JMG code stops in here? **/
- //LWIP_ASSERT("No init function given", init != NULL);
+
+ LWIP_ASSERT("No init function given", init != NULL);
/* reset new interface configuration state */
ip_addr_set_zero(&netif->ip_addr);
@@ -169,11 +171,9 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
/* remember netif specific state information data */
netif->state = state;
- netif->num = netifnum++;
+ netif->num = netif_num++;
netif->input = input;
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = NULL;
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, NULL);
#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
netif->loop_cnt_current = 0;
#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
@@ -196,14 +196,15 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
igmp_start(netif);
}
#endif /* LWIP_IGMP */
-/* LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", netif->name[0], netif->name[1]));
+
+ 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;
}
@@ -271,6 +272,11 @@ netif_remove(struct netif *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") );
}
@@ -323,7 +329,7 @@ netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
struct tcp_pcb_listen *lpcb;
/* address is actually being changed? */
- if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
+ 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;
@@ -362,7 +368,7 @@ netif_set_ipaddr(struct netif *netif, ip_addr_t *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",
+ 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),
@@ -431,8 +437,8 @@ netif_set_default(struct netif *netif)
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] : '\''));
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
}
/**
@@ -448,18 +454,19 @@ void netif_set_up(struct netif *netif)
{
if (!(netif->flags & NETIF_FLAG_UP)) {
netif->flags |= NETIF_FLAG_UP;
+
#if LWIP_SNMP
snmp_get_sysuptime(&netif->ts);
#endif /* LWIP_SNMP */
+
NETIF_STATUS_CALLBACK(netif);
+
if (netif->flags & NETIF_FLAG_LINK_UP) {
#if LWIP_ARP
- /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
- if (netif->flags & (NETIF_FLAG_ETHARP))
- {
- //printf("NETIF_FLAG_LINK_UP, sending gratuitous ARP: %u \n",(netif->flags & NETIF_FLAG_LINK_UP));
- etharp_gratuitous(netif);
- }
+ /* 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
@@ -488,6 +495,11 @@ void netif_set_down(struct netif *netif)
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);
}
}
@@ -504,6 +516,19 @@ void netif_set_status_callback(struct netif *netif, netif_status_callback_fn sta
}
#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+/**
+ * Set callback to be called when the interface has been removed
+ */
+void
+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
*/
diff --git a/Libraries/Lwip/lwip/src/core/pbuf.c b/Libraries/Lwip/lwip/src/core/pbuf.c
index 4917f4c7..ee04dc9a 100644
--- a/Libraries/Lwip/lwip/src/core/pbuf.c
+++ b/Libraries/Lwip/lwip/src/core/pbuf.c
@@ -69,12 +69,12 @@
#include "lwip/src/include/lwip/memp.h"
#include "lwip/src/include/lwip/pbuf.h"
#include "lwip/src/include/lwip/sys.h"
-#include "lwip/src/sam/include/arch/perf.h"
-#if TCP_QUEUE_OOSEQ
+#include "arch/perf.h"
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
#include "lwip/src/include/lwip/tcp_impl.h"
#endif
#if LWIP_CHECKSUM_ON_COPY
-#include "lwip/src/include/lwip/inet_chksum.h"
+#include "lwip/src/include/ipv4/lwip/inet_chksum.h"
#endif
#include <string.h>
@@ -84,18 +84,25 @@
aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
-#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ
#define PBUF_POOL_IS_EMPTY()
-#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
-/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
-#ifndef PBUF_POOL_FREE_OOSEQ
-#define PBUF_POOL_FREE_OOSEQ 1
-#endif /* PBUF_POOL_FREE_OOSEQ */
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
-#if 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()
-static u8_t pbuf_free_ooseq_queued;
+
/**
* Attempt to reclaim some memory from queued out-of-sequence TCP segments
* if we run out of pool pbufs. It's better to give priority to new packets
@@ -104,15 +111,19 @@ static u8_t pbuf_free_ooseq_queued;
* This must be done in the correct thread context therefore this function
* can only be used with NO_SYS=0 and through tcpip_callback.
*/
-static void
-pbuf_free_ooseq(void* arg)
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+void
+pbuf_free_ooseq(void);
+void
+pbuf_free_ooseq(void)
{
struct tcp_pcb* pcb;
SYS_ARCH_DECL_PROTECT(old_level);
- LWIP_UNUSED_ARG(arg);
SYS_ARCH_PROTECT(old_level);
- pbuf_free_ooseq_queued = 0;
+ pbuf_free_ooseq_pending = 0;
SYS_ARCH_UNPROTECT(old_level);
for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
@@ -126,29 +137,42 @@ pbuf_free_ooseq(void* arg)
}
}
+#if !NO_SYS
+/**
+ * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq().
+ */
+static void
+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
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_queued;
- pbuf_free_ooseq_queued = 1;
+ 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 */
- if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) {
- SYS_ARCH_PROTECT(old_level);
- pbuf_free_ooseq_queued = 0;
- SYS_ARCH_UNPROTECT(old_level);
- }
+ PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
}
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
}
-#endif /* PBUF_POOL_FREE_OOSEQ */
-#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
/**
* Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
@@ -190,21 +214,21 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
/* determine header offset */
- offset = 0;
switch (layer) {
case PBUF_TRANSPORT:
/* add room for transport (often TCP) layer header */
- offset += PBUF_TRANSPORT_HLEN;
- /* no break */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+ break;
case PBUF_IP:
/* add room for IP layer header */
- offset += PBUF_IP_HLEN;
- /* no break */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
+ break;
case PBUF_LINK:
/* add room for link layer header */
- offset += PBUF_LINK_HLEN;
+ offset = PBUF_LINK_HLEN;
break;
case PBUF_RAW:
+ offset = 0;
break;
default:
LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
@@ -336,7 +360,8 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
* @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
+ * 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
*/
@@ -348,35 +373,35 @@ pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_cust
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
/* determine header offset */
- offset = 0;
switch (l) {
case PBUF_TRANSPORT:
/* add room for transport (often TCP) layer header */
- offset += PBUF_TRANSPORT_HLEN;
- /* no break */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+ break;
case PBUF_IP:
/* add room for IP layer header */
- offset += PBUF_IP_HLEN;
- /* no break */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
+ break;
case PBUF_LINK:
/* add room for link layer header */
- offset += PBUF_LINK_HLEN;
+ 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) {
+ if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
return NULL;
}
p->pbuf.next = NULL;
if (payload_mem != NULL) {
- p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset));
+ p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
} else {
p->pbuf.payload = NULL;
}
@@ -499,7 +524,7 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
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\n", (increment_magnitude <= p->len), return 1;);
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
} else {
increment_magnitude = header_size_increment;
#if 0
@@ -529,10 +554,10 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
(void *)p->payload, (void *)(p + 1)));
/* restore old payload pointer */
p->payload = payload;
- /* bail out unsuccessfully */
+ /* bail out unsuccesfully */
return 1;
}
- /* pbuf types referring to external payloads? */
+ /* 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)) {
@@ -540,7 +565,7 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
p->payload = (u8_t *)p->payload - header_size_increment;
} else {
/* cannot expand payload to front (yet!)
- * bail out unsuccessfully */
+ * bail out unsuccesfully */
return 1;
}
} else {
@@ -552,8 +577,8 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
p->len += header_size_increment;
p->tot_len += header_size_increment;
-// LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %d new %d (%d)\n",
-// (void *)payload, (void *)p->payload, 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;
}
@@ -840,7 +865,6 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
/* iterate through pbuf chain */
do
{
- LWIP_ASSERT("p_to != NULL", p_to != NULL);
/* copy one part of the original chain */
if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
/* complete current p_from fits into current p_to */
@@ -853,17 +877,18 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
offset_to += len;
offset_from += len;
LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
- if (offset_to == p_to->len) {
- /* on to next p_to (if any) */
- offset_to = 0;
- p_to = p_to->next;
- }
LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
if (offset_from >= p_from->len) {
/* on to next p_from (if any) */
offset_from = 0;
p_from = p_from->next;
}
+ if (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! */
@@ -997,7 +1022,6 @@ pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
return p;
}
err = pbuf_copy(q, p);
- (void)err; // suppress compiler warning
LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
pbuf_free(p);
return q;
diff --git a/Libraries/Lwip/lwip/src/core/raw.c b/Libraries/Lwip/lwip/src/core/raw.c
index 61cffd7e..74a21ade 100644
--- a/Libraries/Lwip/lwip/src/core/raw.c
+++ b/Libraries/Lwip/lwip/src/core/raw.c
@@ -48,7 +48,7 @@
#include "lwip/src/include/lwip/netif.h"
#include "lwip/src/include/lwip/raw.h"
#include "lwip/src/include/lwip/stats.h"
-#include "lwip/src/sam/include/arch/perf.h"
+#include "arch/perf.h"
#include <string.h>
@@ -95,7 +95,7 @@ raw_input(struct pbuf *p, struct netif *inp)
ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest))) {
#if IP_SOF_BROADCAST_RECV
/* broadcast filter? */
- if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(&current_iphdr_dest, inp))
+ if (ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(&current_iphdr_dest, inp))
#endif /* IP_SOF_BROADCAST_RECV */
{
/* receive callback function available? */
@@ -245,7 +245,7 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
#if IP_SOF_BROADCAST
/* broadcast filter? */
- if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) {
+ 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) {
@@ -263,13 +263,9 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
src_ip = &(pcb->local_ip);
}
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = &(pcb->addr_hint);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = NULL;
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, NULL);
/* did we chain a header earlier? */
if (q != p) {
diff --git a/Libraries/Lwip/lwip/src/core/snmp/asn1_dec.c b/Libraries/Lwip/lwip/src/core/snmp/asn1_dec.c
new file mode 100644
index 00000000..1d565820
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/snmp/asn1_dec.c
@@ -0,0 +1,657 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) decoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Retrieves type field from incoming pbuf chain.
+ *
+ * @param p points to a pbuf holding an ASN1 coded type field
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
+ * @param type return ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *type = *msg_ptr;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes length field from incoming pbuf chain into host length.
+ *
+ * @param p points to a pbuf holding an ASN1 coded length
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
+ * @param octets_used returns number of octets used by the length code
+ * @param length return host order length, upto 64k
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (*msg_ptr < 0x80)
+ {
+ /* primitive definite length format */
+ *octets_used = 1;
+ *length = *msg_ptr;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x80)
+ {
+ /* constructed indefinite length format, termination with two zero octets */
+ u8_t zeros;
+ u8_t i;
+
+ *length = 0;
+ zeros = 0;
+ while (zeros != 2)
+ {
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ (*length) += 1;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (*msg_ptr == 0)
+ {
+ zeros++;
+ if (zeros == 2)
+ {
+ /* stop while (i > 0) */
+ i = 0;
+ }
+ }
+ else
+ {
+ zeros = 0;
+ }
+ }
+ }
+ *octets_used = 1;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x81)
+ {
+ /* constructed definite length format, one octet */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *length = *msg_ptr;
+ *octets_used = 2;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x82)
+ {
+ u8_t i;
+
+ /* constructed definite length format, two octets */
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *length |= *msg_ptr;
+ }
+ else
+ {
+ /* most significant length octet */
+ *length = (*msg_ptr) << 8;
+ }
+ }
+ *octets_used = 3;
+ return ERR_OK;
+ }
+ else
+ {
+ /* constructed definite length format 3..127 octets, this is too big (>64k) */
+ /** @todo: do we need to accept inefficient codings with many leading zero's? */
+ *octets_used = 1 + ((*msg_ptr) & 0x7f);
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 6))
+ {
+ /* start from zero */
+ *value = 0;
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, expecting zero sign bit! */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* positive */
+ if ((len > 1) && (*msg_ptr == 0))
+ {
+ /* skip leading "sign byte" octet 0x00 */
+ len--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ }
+ /* OR octets with value */
+ while (len > 1)
+ {
+ len--;
+ *value |= *msg_ptr;
+ *value <<= 8;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ *value |= *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
+#endif
+ u8_t sign;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 5))
+ {
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, start from -1 */
+ *value = -1;
+ sign = 1;
+ }
+ else
+ {
+ /* positive, start from 0 */
+ *value = 0;
+ sign = 0;
+ }
+ /* OR/AND octets with value */
+ while (len > 1)
+ {
+ len--;
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ *value <<= 8;
+ *lsb_ptr |= 255;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ *value <<= 8;
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ }
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded object identifier
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
+ * @param len length of the coded object identifier
+ * @param oid return object identifier struct
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+ s32_t *oid_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ oid->len = 0;
+ oid_ptr = &oid->id[0];
+ if (len > 0)
+ {
+ /* first compressed octet */
+ if (*msg_ptr == 0x2B)
+ {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 40)
+ {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = *msg_ptr;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 80)
+ {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 40;
+ oid_ptr++;
+ }
+ else
+ {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 80;
+ oid_ptr++;
+ }
+ oid->len = 2;
+ }
+ else
+ {
+ /* accepting zero length identifiers e.g. for
+ getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+ len--;
+ if (len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
+ {
+ /* sub-identifier uses multiple octets */
+ if (*msg_ptr & 0x80)
+ {
+ s32_t sub_id = 0;
+
+ while ((*msg_ptr & 0x80) && (len > 1))
+ {
+ len--;
+ sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (!(*msg_ptr & 0x80) && (len > 0))
+ {
+ /* last octet sub-identifier */
+ len--;
+ sub_id = (sub_id << 7) + *msg_ptr;
+ *oid_ptr = sub_id;
+ }
+ }
+ else
+ {
+ /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
+ len--;
+ *oid_ptr = *msg_ptr;
+ }
+ if (len > 0)
+ {
+ /* remaining oid bytes available ... */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ oid_ptr++;
+ oid->len++;
+ }
+ if (len == 0)
+ {
+ /* len == 0, end of oid */
+ return ERR_OK;
+ }
+ else
+ {
+ /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
+ return ERR_ARG;
+ }
+
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param p points to a pbuf holding an ASN1 coded raw data
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param raw_len length of the raw return value
+ * @param raw return raw bytes
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ if (len > 0)
+ {
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if (raw_len >= len)
+ {
+ while (len > 1)
+ {
+ /* copy len - 1 octets */
+ len--;
+ *raw = *msg_ptr;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* copy last octet */
+ *raw = *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ /* raw_len < len, not enough dst space */
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* len == 0, empty string */
+ return ERR_OK;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/Libraries/Lwip/lwip/src/core/snmp/asn1_enc.c b/Libraries/Lwip/lwip/src/core/snmp/asn1_enc.c
new file mode 100644
index 00000000..64dfc5f6
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/snmp/asn1_enc.c
@@ -0,0 +1,611 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+ if (length < 0x80U)
+ {
+ *octets_needed = 1;
+ }
+ else if (length < 0x100U)
+ {
+ *octets_needed = 2;
+ }
+ else
+ {
+ *octets_needed = 3;
+ }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+ if (value < 0x80UL)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000UL)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000UL)
+ {
+ *octets_needed = 3;
+ }
+ else if (value < 0x80000000UL)
+ {
+ *octets_needed = 4;
+ }
+ else
+ {
+ *octets_needed = 5;
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+ if (value < 0)
+ {
+ value = ~value;
+ }
+ if (value < 0x80L)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000L)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000L)
+ {
+ *octets_needed = 3;
+ }
+ else
+ {
+ *octets_needed = 4;
+ }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
+{
+ s32_t sub_id;
+ u8_t cnt;
+
+ cnt = 0;
+ if (ident_len > 1)
+ {
+ /* compressed prefix in one octet */
+ cnt++;
+ ident_len -= 2;
+ ident += 2;
+ }
+ while(ident_len > 0)
+ {
+ ident_len--;
+ sub_id = *ident;
+
+ sub_id >>= 7;
+ cnt++;
+ while(sub_id > 0)
+ {
+ sub_id >>= 7;
+ cnt++;
+ }
+ ident++;
+ }
+ *octets_needed = cnt;
+}
+
+/**
+ * Encodes ASN type field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param type input ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *msg_ptr = type;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes host order length field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode length into
+ * @param ofs points to the offset within the pbuf chain
+ * @param length is the host order length to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (length < 0x80)
+ {
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else if (length < 0x100)
+ {
+ *msg_ptr = 0x81;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else
+ {
+ u8_t i;
+
+ /* length >= 0x100 && length <= 0xFFFF */
+ *msg_ptr = 0x82;
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *msg_ptr = (u8_t)length;
+ }
+ else
+ {
+ /* most significant length octet */
+ *msg_ptr = (u8_t)(length >> 8);
+ }
+ }
+ return ERR_OK;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (octets_needed == 5)
+ {
+ /* not enough bits in 'value' add leading 0x00 */
+ octets_needed--;
+ *msg_ptr = 0x00;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode oid into
+ * @param ofs points to the offset within the pbuf chain
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (ident_len > 1)
+ {
+ if ((ident[0] == 1) && (ident[1] == 3))
+ {
+ /* compressed (most common) prefix .iso.org */
+ *msg_ptr = 0x2b;
+ }
+ else
+ {
+ /* calculate prefix */
+ *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ ident_len -= 2;
+ ident += 2;
+ }
+ else
+ {
+/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+ /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+ return ERR_ARG;
+ }
+ while (ident_len > 0)
+ {
+ s32_t sub_id;
+ u8_t shift, tail;
+
+ ident_len--;
+ sub_id = *ident;
+ tail = 0;
+ shift = 28;
+ while(shift > 0)
+ {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0))
+ {
+ tail = 1;
+ *msg_ptr = code | 0x80;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ shift -= 7;
+ }
+ *msg_ptr = (u8_t)sub_id & 0x7F;
+ if (ident_len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* proceed to next sub-identifier */
+ ident++;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode raw data into
+ * @param ofs points to the offset within the pbuf chain
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (raw_len > 1)
+ {
+ /* copy raw_len - 1 octets */
+ raw_len--;
+ *msg_ptr = *raw;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (raw_len > 0)
+ {
+ /* copy last or single octet */
+ *msg_ptr = *raw;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/Libraries/Lwip/lwip/src/core/snmp/mib2.c b/Libraries/Lwip/lwip/src/core/snmp/mib2.c
new file mode 100644
index 00000000..4775ba99
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/snmp/mib2.c
@@ -0,0 +1,4146 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ *
+ * @note the object identifiers for this MIB-2 and private MIB tree
+ * must be kept in sorted ascending order. This to ensure correct getnext operation.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/mem.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/udp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/sys.h"
+#include "netif/etharp.h"
+
+/**
+ * IANA assigned enterprise ID for lwIP is 26381
+ * @see http://www.iana.org/assignments/enterprise-numbers
+ *
+ * @note this enterprise ID is assigned to the lwIP project,
+ * all object identifiers living under this ID are assigned
+ * by the lwIP maintainers (contact Christiaan Simons)!
+ * @note don't change this define, use snmp_set_sysobjid()
+ *
+ * If you need to create your own private MIB you'll need
+ * to apply for your own enterprise ID with IANA:
+ * http://www.iana.org/numbers.html
+ */
+#define SNMP_ENTERPRISE_ID 26381
+#define SNMP_SYSOBJID_LEN 7
+#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID}
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+#ifndef SNMP_GET_SYSUPTIME
+#define SNMP_GET_SYSUPTIME(sysuptime) (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(&pcb->local_ip, &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ udp_rn = &udp_root;
+ for (level = 0; level < 5; level++)
+ {
+ udp_node = NULL;
+ snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node);
+ if ((level != 4) && (udp_node != NULL))
+ {
+ if (udp_node->nptr == NULL)
+ {
+ udp_rn = snmp_mib_lrn_alloc();
+ udp_node->nptr = (struct mib_node*)udp_rn;
+ if (udp_rn != NULL)
+ {
+ if (level == 3)
+ {
+ udp_rn->get_object_def = udpentry_get_object_def;
+ udp_rn->get_value = udpentry_get_value;
+ udp_rn->set_test = noleafs_set_test;
+ udp_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* udp_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ udp_rn = (struct mib_list_rootnode*)udp_node->nptr;
+ }
+ }
+ }
+ udptable.maxlength = 1;
+}
+
+/**
+ * Removes udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * from index tree.
+ */
+void snmp_delete_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct udp_pcb *npcb;
+ struct mib_list_rootnode *udp_rn, *next, *del_rn[5];
+ struct mib_list_node *udp_n, *del_n[5];
+ s32_t udpidx[5];
+ u8_t bindings, fc, level, del_cnt;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(&pcb->local_ip, &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ /* count PCBs for a given binding
+ (e.g. when reusing ports or for temp output PCBs) */
+ bindings = 0;
+ npcb = udp_pcbs;
+ while ((npcb != NULL))
+ {
+ if (ip_addr_cmp(&npcb->local_ip, &pcb->local_ip) &&
+ (npcb->local_port == udpidx[4]))
+ {
+ bindings++;
+ }
+ npcb = npcb->next;
+ }
+ if (bindings == 1)
+ {
+ /* selectively remove */
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ udp_rn = &udp_root;
+ while ((level < 5) && (udp_rn != NULL))
+ {
+ fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n);
+ if (fc == 0)
+ {
+ /* udpidx[level] does not exist */
+ del_cnt = 0;
+ udp_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = udp_rn;
+ del_n[del_cnt] = udp_n;
+ del_cnt++;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ udp_rn = del_rn[del_cnt];
+ udp_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(udp_rn, udp_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (udp_root.count == 0) udptable.maxlength = 0;
+}
+
+
+void snmp_inc_snmpinpkts(void)
+{
+ snmpinpkts++;
+}
+
+void snmp_inc_snmpoutpkts(void)
+{
+ snmpoutpkts++;
+}
+
+void snmp_inc_snmpinbadversions(void)
+{
+ snmpinbadversions++;
+}
+
+void snmp_inc_snmpinbadcommunitynames(void)
+{
+ snmpinbadcommunitynames++;
+}
+
+void snmp_inc_snmpinbadcommunityuses(void)
+{
+ snmpinbadcommunityuses++;
+}
+
+void snmp_inc_snmpinasnparseerrs(void)
+{
+ snmpinasnparseerrs++;
+}
+
+void snmp_inc_snmpintoobigs(void)
+{
+ snmpintoobigs++;
+}
+
+void snmp_inc_snmpinnosuchnames(void)
+{
+ snmpinnosuchnames++;
+}
+
+void snmp_inc_snmpinbadvalues(void)
+{
+ snmpinbadvalues++;
+}
+
+void snmp_inc_snmpinreadonlys(void)
+{
+ snmpinreadonlys++;
+}
+
+void snmp_inc_snmpingenerrs(void)
+{
+ snmpingenerrs++;
+}
+
+void snmp_add_snmpintotalreqvars(u8_t value)
+{
+ snmpintotalreqvars += value;
+}
+
+void snmp_add_snmpintotalsetvars(u8_t value)
+{
+ snmpintotalsetvars += value;
+}
+
+void snmp_inc_snmpingetrequests(void)
+{
+ snmpingetrequests++;
+}
+
+void snmp_inc_snmpingetnexts(void)
+{
+ snmpingetnexts++;
+}
+
+void snmp_inc_snmpinsetrequests(void)
+{
+ snmpinsetrequests++;
+}
+
+void snmp_inc_snmpingetresponses(void)
+{
+ snmpingetresponses++;
+}
+
+void snmp_inc_snmpintraps(void)
+{
+ snmpintraps++;
+}
+
+void snmp_inc_snmpouttoobigs(void)
+{
+ snmpouttoobigs++;
+}
+
+void snmp_inc_snmpoutnosuchnames(void)
+{
+ snmpoutnosuchnames++;
+}
+
+void snmp_inc_snmpoutbadvalues(void)
+{
+ snmpoutbadvalues++;
+}
+
+void snmp_inc_snmpoutgenerrs(void)
+{
+ snmpoutgenerrs++;
+}
+
+void snmp_inc_snmpoutgetrequests(void)
+{
+ snmpoutgetrequests++;
+}
+
+void snmp_inc_snmpoutgetnexts(void)
+{
+ snmpoutgetnexts++;
+}
+
+void snmp_inc_snmpoutsetrequests(void)
+{
+ snmpoutsetrequests++;
+}
+
+void snmp_inc_snmpoutgetresponses(void)
+{
+ snmpoutgetresponses++;
+}
+
+void snmp_inc_snmpouttraps(void)
+{
+ snmpouttraps++;
+}
+
+void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid)
+{
+ *oid = &snmpgrp_id;
+}
+
+void snmp_set_snmpenableauthentraps(u8_t *value)
+{
+ if (value != NULL)
+ {
+ snmpenableauthentraps_ptr = value;
+ }
+}
+
+void snmp_get_snmpenableauthentraps(u8_t *value)
+{
+ *value = *snmpenableauthentraps_ptr;
+}
+
+void
+noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ LWIP_UNUSED_ARG(ident_len);
+ LWIP_UNUSED_ARG(ident);
+ od->instance = MIB_OBJECT_NONE;
+}
+
+void
+noleafs_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+}
+
+u8_t
+noleafs_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+ /* can't set */
+ return 0;
+}
+
+void
+noleafs_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+}
+
+
+/**
+ * Returns systems object definitions.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param od points to object definition.
+ */
+static void
+system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* sysDescr */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *sysdescr_len_ptr;
+ break;
+ case 2: /* sysObjectID */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = sysobjid.len * sizeof(s32_t);
+ break;
+ case 3: /* sysUpTime */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 4: /* sysContact */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *syscontact_len_ptr;
+ break;
+ case 5: /* sysName */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *sysname_len_ptr;
+ break;
+ case 6: /* sysLocation */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *syslocation_len_ptr;
+ break;
+ case 7: /* sysServices */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns system object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+system_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* sysDescr */
+ ocstrncpy((u8_t*)value, sysdescr_ptr, len);
+ break;
+ case 2: /* sysObjectID */
+ objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ case 3: /* sysUpTime */
+ {
+ snmp_get_sysuptime((u32_t*)value);
+ }
+ break;
+ case 4: /* sysContact */
+ ocstrncpy((u8_t*)value, syscontact_ptr, len);
+ break;
+ case 5: /* sysName */
+ ocstrncpy((u8_t*)value, sysname_ptr, len);
+ break;
+ case 6: /* sysLocation */
+ ocstrncpy((u8_t*)value, syslocation_ptr, len);
+ break;
+ case 7: /* sysServices */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = sysservices;
+ }
+ break;
+ };
+}
+
+static u8_t
+system_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+
+ LWIP_UNUSED_ARG(value);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 4: /* sysContact */
+ if ((syscontact_ptr != syscontact_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ case 5: /* sysName */
+ if ((sysname_ptr != sysname_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ case 6: /* sysLocation */
+ if ((syslocation_ptr != syslocation_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ };
+ return set_ok;
+}
+
+static void
+system_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_ASSERT("invalid len", len <= 0xff);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 4: /* sysContact */
+ ocstrncpy(syscontact_ptr, (u8_t*)value, len);
+ *syscontact_len_ptr = (u8_t)len;
+ break;
+ case 5: /* sysName */
+ ocstrncpy(sysname_ptr, (u8_t*)value, len);
+ *sysname_len_ptr = (u8_t)len;
+ break;
+ case 6: /* sysLocation */
+ ocstrncpy(syslocation_ptr, (u8_t*)value, len);
+ *syslocation_len_ptr = (u8_t)len;
+ break;
+ };
+}
+
+/**
+ * Returns interfaces.ifnumber object definition.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.index
+ * @param od points to object definition.
+ */
+static void
+interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns interfaces.ifnumber object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+interfaces_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(len);
+ if (od->id_inst_ptr[0] == 1)
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = iflist_root.count;
+ }
+}
+
+/**
+ * Returns ifentry object definitions.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.index
+ * @param od points to object definition.
+ */
+static void
+ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* ifIndex */
+ case 3: /* ifType */
+ case 4: /* ifMtu */
+ case 8: /* ifOperStatus */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* ifDescr */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ /** @todo this should be some sort of sizeof(struct netif.name) */
+ od->v_len = 2;
+ break;
+ case 5: /* ifSpeed */
+ case 21: /* ifOutQLen */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 6: /* ifPhysAddress */
+ {
+ struct netif *netif;
+
+ snmp_ifindextonetif(ident[1], &netif);
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = netif->hwaddr_len;
+ }
+ break;
+ case 7: /* ifAdminStatus */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 9: /* ifLastChange */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 10: /* ifInOctets */
+ case 11: /* ifInUcastPkts */
+ case 12: /* ifInNUcastPkts */
+ case 13: /* ifInDiscarts */
+ case 14: /* ifInErrors */
+ case 15: /* ifInUnkownProtos */
+ case 16: /* ifOutOctets */
+ case 17: /* ifOutUcastPkts */
+ case 18: /* ifOutNUcastPkts */
+ case 19: /* ifOutDiscarts */
+ case 20: /* ifOutErrors */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 22: /* ifSpecific */
+ /** @note returning zeroDotZero (0.0) no media specific MIB support */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = ifspecific.len * sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns ifentry object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+ifentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id;
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ifIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* ifDescr */
+ ocstrncpy((u8_t*)value, (u8_t*)netif->name, len);
+ break;
+ case 3: /* ifType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = netif->link_type;
+ }
+ break;
+ case 4: /* ifMtu */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = netif->mtu;
+ }
+ break;
+ case 5: /* ifSpeed */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->link_speed;
+ }
+ break;
+ case 6: /* ifPhysAddress */
+ ocstrncpy((u8_t*)value, netif->hwaddr, len);
+ break;
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (netif_is_up(netif))
+ {
+ if (netif_is_link_up(netif))
+ {
+ *sint_ptr = 1; /* up */
+ }
+ else
+ {
+ *sint_ptr = 7; /* lowerLayerDown */
+ }
+ }
+ else
+ {
+ *sint_ptr = 2; /* down */
+ }
+ }
+ break;
+ case 8: /* ifOperStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (netif_is_up(netif))
+ {
+ *sint_ptr = 1;
+ }
+ else
+ {
+ *sint_ptr = 2;
+ }
+ }
+ break;
+ case 9: /* ifLastChange */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ts;
+ }
+ break;
+ case 10: /* ifInOctets */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinoctets;
+ }
+ break;
+ case 11: /* ifInUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinucastpkts;
+ }
+ break;
+ case 12: /* ifInNUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinnucastpkts;
+ }
+ break;
+ case 13: /* ifInDiscarts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifindiscards;
+ }
+ break;
+ case 14: /* ifInErrors */
+ case 15: /* ifInUnkownProtos */
+ /** @todo add these counters! */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 16: /* ifOutOctets */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutoctets;
+ }
+ break;
+ case 17: /* ifOutUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutucastpkts;
+ }
+ break;
+ case 18: /* ifOutNUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutnucastpkts;
+ }
+ break;
+ case 19: /* ifOutDiscarts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutdiscards;
+ }
+ break;
+ case 20: /* ifOutErrors */
+ /** @todo add this counter! */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 21: /* ifOutQLen */
+ /** @todo figure out if this must be 0 (no queue) or 1? */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 22: /* ifSpecific */
+ objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ };
+}
+
+#if !SNMP_SAFE_REQUESTS
+static u8_t
+ifentry_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id, set_ok;
+ LWIP_UNUSED_ARG(len);
+
+ set_ok = 0;
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == 1 || *sint_ptr == 2)
+ set_ok = 1;
+ }
+ break;
+ }
+ return set_ok;
+}
+
+static void
+ifentry_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id;
+ LWIP_UNUSED_ARG(len);
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == 1)
+ {
+ netif_set_up(netif);
+ }
+ else if (*sint_ptr == 2)
+ {
+ netif_set_down(netif);
+ }
+ }
+ break;
+ }
+}
+#endif /* SNMP_SAFE_REQUESTS */
+
+/**
+ * Returns atentry object definitions.
+ *
+ * @param ident_len the address length (6)
+ * @param ident points to objectname.atifindex.atnetaddress
+ * @param od points to object definition.
+ */
+static void
+atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* atIfIndex */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* atPhysAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = 6; /** @todo try to use netif::hwaddr_len */
+ break;
+ case 3: /* atNetAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+atentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+#if LWIP_ARP
+ u8_t id;
+ struct eth_addr* ethaddr_ret;
+ ip_addr_t* ipaddr_ret;
+#endif /* LWIP_ARP */
+ ip_addr_t ip;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ snmp_oidtoip(&od->id_inst_ptr[2], &ip);
+
+#if LWIP_ARP /** @todo implement a netif_find_addr */
+ if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* atIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* atPhysAddress */
+ {
+ struct eth_addr *dst = (struct eth_addr*)value;
+
+ *dst = *ethaddr_ret;
+ }
+ break;
+ case 3: /* atNetAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ *dst = *ipaddr_ret;
+ }
+ break;
+ }
+ }
+#endif /* LWIP_ARP */
+}
+
+static void
+ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+ case 2: /* ipDefaultTTL */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 3: /* ipInReceives */
+ case 4: /* ipInHdrErrors */
+ case 5: /* ipInAddrErrors */
+ case 6: /* ipForwDatagrams */
+ case 7: /* ipInUnknownProtos */
+ case 8: /* ipInDiscards */
+ case 9: /* ipInDelivers */
+ case 10: /* ipOutRequests */
+ case 11: /* ipOutDiscards */
+ case 12: /* ipOutNoRoutes */
+ case 14: /* ipReasmReqds */
+ case 15: /* ipReasmOKs */
+ case 16: /* ipReasmFails */
+ case 17: /* ipFragOKs */
+ case 18: /* ipFragFails */
+ case 19: /* ipFragCreates */
+ case 23: /* ipRoutingDiscards */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 13: /* ipReasmTimeout */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_FORWARD
+ /* forwarding */
+ *sint_ptr = 1;
+#else
+ /* not-forwarding */
+ *sint_ptr = 2;
+#endif
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = IP_DEFAULT_TTL;
+ }
+ break;
+ case 3: /* ipInReceives */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinreceives;
+ }
+ break;
+ case 4: /* ipInHdrErrors */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinhdrerrors;
+ }
+ break;
+ case 5: /* ipInAddrErrors */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinaddrerrors;
+ }
+ break;
+ case 6: /* ipForwDatagrams */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipforwdatagrams;
+ }
+ break;
+ case 7: /* ipInUnknownProtos */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinunknownprotos;
+ }
+ break;
+ case 8: /* ipInDiscards */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipindiscards;
+ }
+ break;
+ case 9: /* ipInDelivers */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipindelivers;
+ }
+ break;
+ case 10: /* ipOutRequests */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutrequests;
+ }
+ break;
+ case 11: /* ipOutDiscards */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutdiscards;
+ }
+ break;
+ case 12: /* ipOutNoRoutes */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutnoroutes;
+ }
+ break;
+ case 13: /* ipReasmTimeout */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_REASSEMBLY
+ *sint_ptr = IP_REASS_MAXAGE;
+#else
+ *sint_ptr = 0;
+#endif
+ }
+ break;
+ case 14: /* ipReasmReqds */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmreqds;
+ }
+ break;
+ case 15: /* ipReasmOKs */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmoks;
+ }
+ break;
+ case 16: /* ipReasmFails */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmfails;
+ }
+ break;
+ case 17: /* ipFragOKs */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragoks;
+ }
+ break;
+ case 18: /* ipFragFails */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragfails;
+ }
+ break;
+ case 19: /* ipFragCreates */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragcreates;
+ }
+ break;
+ case 23: /* ipRoutingDiscards */
+ /** @todo can lwIP discard routes at all?? hardwire this to 0?? */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = iproutingdiscards;
+ }
+ break;
+ };
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param od is the object definition
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ *
+ * @note we allow set if the value matches the hardwired value,
+ * otherwise return badvalue.
+ */
+static u8_t
+ip_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+ s32_t *sint_ptr = (s32_t*)value;
+
+ LWIP_UNUSED_ARG(len);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ if (*sint_ptr == 1)
+#else
+ /* not-forwarding */
+ if (*sint_ptr == 2)
+#endif
+ {
+ set_ok = 1;
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ if (*sint_ptr == IP_DEFAULT_TTL)
+ {
+ set_ok = 1;
+ }
+ break;
+ };
+ return set_ok;
+}
+
+static void
+ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (4) */
+ ident_len += 4;
+ ident -= 4;
+
+ if (ident_len == 5)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipAdEntAddr */
+ case 3: /* ipAdEntNetMask */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* ipAdEntIfIndex */
+ case 4: /* ipAdEntBcastAddr */
+ case 5: /* ipAdEntReasmMaxSize */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ u16_t ifidx;
+ ip_addr_t ip;
+ struct netif *netif = netif_list;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], &ip);
+ ifidx = 0;
+ while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr))
+ {
+ netif = netif->next;
+ ifidx++;
+ }
+
+ if (netif != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipAdEntAddr */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = netif->ip_addr;
+ }
+ break;
+ case 2: /* ipAdEntIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = ifidx + 1;
+ }
+ break;
+ case 3: /* ipAdEntNetMask */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = netif->netmask;
+ }
+ break;
+ case 4: /* ipAdEntBcastAddr */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ /* lwIP oddity, there's no broadcast
+ address in the netif we can rely on */
+ *sint_ptr = IPADDR_BROADCAST & 1;
+ }
+ break;
+ case 5: /* ipAdEntReasmMaxSize */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_REASSEMBLY
+ /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
+ * but only if receiving one fragmented packet at a time.
+ * The current solution is to calculate for 2 simultaneous packets...
+ */
+ *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
+ (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN)));
+#else
+ /** @todo returning MTU would be a bad thing and
+ returning a wild guess like '576' isn't good either */
+ *sint_ptr = 0;
+#endif
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * @note
+ * lwIP IP routing is currently using the network addresses in netif_list.
+ * if no suitable network IP is found in netif_list, the default_netif is used.
+ */
+static void
+ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (4) */
+ ident_len += 4;
+ ident -= 4;
+
+ if (ident_len == 5)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipRouteDest */
+ case 7: /* ipRouteNextHop */
+ case 11: /* ipRouteMask */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* ipRouteIfIndex */
+ case 3: /* ipRouteMetric1 */
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ case 8: /* ipRouteType */
+ case 10: /* ipRouteAge */
+ case 12: /* ipRouteMetric5 */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 9: /* ipRouteProto */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 13: /* ipRouteInfo */
+ /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = iprouteinfo.len * sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ ip_addr_t dest;
+ s32_t *ident;
+ u8_t id;
+
+ ident = od->id_inst_ptr;
+ snmp_oidtoip(&ident[1], &dest);
+
+ if (ip_addr_isany(&dest))
+ {
+ /* ip_route() uses default netif for default route */
+ netif = netif_default;
+ }
+ else
+ {
+ /* not using ip_route(), need exact match! */
+ netif = netif_list;
+ while ((netif != NULL) &&
+ !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) )
+ {
+ netif = netif->next;
+ }
+ }
+ if (netif != NULL)
+ {
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipRouteDest */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte has 0.0.0.0 dest */
+ ip_addr_set_zero(dst);
+ }
+ else
+ {
+ /* netifs have netaddress dest */
+ ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask);
+ }
+ }
+ break;
+ case 2: /* ipRouteIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ snmp_netiftoifindex(netif, sint_ptr);
+ }
+ break;
+ case 3: /* ipRouteMetric1 */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte has metric 1 */
+ *sint_ptr = 1;
+ }
+ else
+ {
+ /* other rtes have metric 0 */
+ *sint_ptr = 0;
+ }
+ }
+ break;
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ case 12: /* ipRouteMetric5 */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* not used */
+ *sint_ptr = -1;
+ }
+ break;
+ case 7: /* ipRouteNextHop */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte: gateway */
+ *dst = netif->gw;
+ }
+ else
+ {
+ /* other rtes: netif ip_addr */
+ *dst = netif->ip_addr;
+ }
+ }
+ break;
+ case 8: /* ipRouteType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte is indirect */
+ *sint_ptr = 4;
+ }
+ else
+ {
+ /* other rtes are direct */
+ *sint_ptr = 3;
+ }
+ }
+ break;
+ case 9: /* ipRouteProto */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* locally defined routes */
+ *sint_ptr = 2;
+ }
+ break;
+ case 10: /* ipRouteAge */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /** @todo (sysuptime - timestamp last change) / 100
+ @see snmp_insert_iprteidx_tree() */
+ *sint_ptr = 0;
+ }
+ break;
+ case 11: /* ipRouteMask */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte use 0.0.0.0 mask */
+ ip_addr_set_zero(dst);
+ }
+ else
+ {
+ /* other rtes use netmask */
+ *dst = netif->netmask;
+ }
+ }
+ break;
+ case 13: /* ipRouteInfo */
+ objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ }
+ }
+}
+
+static void
+ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipNetToMediaIfIndex */
+ case 4: /* ipNetToMediaType */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* ipNetToMediaPhysAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = 6; /** @todo try to use netif::hwaddr_len */
+ break;
+ case 3: /* ipNetToMediaNetAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+#if LWIP_ARP
+ u8_t id;
+ struct eth_addr* ethaddr_ret;
+ ip_addr_t* ipaddr_ret;
+#endif /* LWIP_ARP */
+ ip_addr_t ip;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ snmp_oidtoip(&od->id_inst_ptr[2], &ip);
+
+#if LWIP_ARP /** @todo implement a netif_find_addr */
+ if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipNetToMediaIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* ipNetToMediaPhysAddress */
+ {
+ struct eth_addr *dst = (struct eth_addr*)value;
+
+ *dst = *ethaddr_ret;
+ }
+ break;
+ case 3: /* ipNetToMediaNetAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ *dst = *ipaddr_ret;
+ }
+ break;
+ case 4: /* ipNetToMediaType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* dynamic (?) */
+ *sint_ptr = 3;
+ }
+ break;
+ }
+ }
+#endif /* LWIP_ARP */
+}
+
+static void
+icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if ((ident_len == 2) &&
+ (ident[0] > 0) && (ident[0] < 27))
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+icmp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* icmpInMsgs */
+ *uint_ptr = icmpinmsgs;
+ break;
+ case 2: /* icmpInErrors */
+ *uint_ptr = icmpinerrors;
+ break;
+ case 3: /* icmpInDestUnreachs */
+ *uint_ptr = icmpindestunreachs;
+ break;
+ case 4: /* icmpInTimeExcds */
+ *uint_ptr = icmpintimeexcds;
+ break;
+ case 5: /* icmpInParmProbs */
+ *uint_ptr = icmpinparmprobs;
+ break;
+ case 6: /* icmpInSrcQuenchs */
+ *uint_ptr = icmpinsrcquenchs;
+ break;
+ case 7: /* icmpInRedirects */
+ *uint_ptr = icmpinredirects;
+ break;
+ case 8: /* icmpInEchos */
+ *uint_ptr = icmpinechos;
+ break;
+ case 9: /* icmpInEchoReps */
+ *uint_ptr = icmpinechoreps;
+ break;
+ case 10: /* icmpInTimestamps */
+ *uint_ptr = icmpintimestamps;
+ break;
+ case 11: /* icmpInTimestampReps */
+ *uint_ptr = icmpintimestampreps;
+ break;
+ case 12: /* icmpInAddrMasks */
+ *uint_ptr = icmpinaddrmasks;
+ break;
+ case 13: /* icmpInAddrMaskReps */
+ *uint_ptr = icmpinaddrmaskreps;
+ break;
+ case 14: /* icmpOutMsgs */
+ *uint_ptr = icmpoutmsgs;
+ break;
+ case 15: /* icmpOutErrors */
+ *uint_ptr = icmpouterrors;
+ break;
+ case 16: /* icmpOutDestUnreachs */
+ *uint_ptr = icmpoutdestunreachs;
+ break;
+ case 17: /* icmpOutTimeExcds */
+ *uint_ptr = icmpouttimeexcds;
+ break;
+ case 18: /* icmpOutParmProbs */
+ *uint_ptr = icmpoutparmprobs;
+ break;
+ case 19: /* icmpOutSrcQuenchs */
+ *uint_ptr = icmpoutsrcquenchs;
+ break;
+ case 20: /* icmpOutRedirects */
+ *uint_ptr = icmpoutredirects;
+ break;
+ case 21: /* icmpOutEchos */
+ *uint_ptr = icmpoutechos;
+ break;
+ case 22: /* icmpOutEchoReps */
+ *uint_ptr = icmpoutechoreps;
+ break;
+ case 23: /* icmpOutTimestamps */
+ *uint_ptr = icmpouttimestamps;
+ break;
+ case 24: /* icmpOutTimestampReps */
+ *uint_ptr = icmpouttimestampreps;
+ break;
+ case 25: /* icmpOutAddrMasks */
+ *uint_ptr = icmpoutaddrmasks;
+ break;
+ case 26: /* icmpOutAddrMaskReps */
+ *uint_ptr = icmpoutaddrmaskreps;
+ break;
+ }
+}
+
+#if LWIP_TCP
+/** @todo tcp grp */
+static void
+tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
+
+ switch (id)
+ {
+ case 1: /* tcpRtoAlgorithm */
+ case 2: /* tcpRtoMin */
+ case 3: /* tcpRtoMax */
+ case 4: /* tcpMaxConn */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 5: /* tcpActiveOpens */
+ case 6: /* tcpPassiveOpens */
+ case 7: /* tcpAttemptFails */
+ case 8: /* tcpEstabResets */
+ case 10: /* tcpInSegs */
+ case 11: /* tcpOutSegs */
+ case 12: /* tcpRetransSegs */
+ case 14: /* tcpInErrs */
+ case 15: /* tcpOutRsts */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 9: /* tcpCurrEstab */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
+ od->v_len = sizeof(u32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+tcp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ s32_t *sint_ptr = (s32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* tcpRtoAlgorithm, vanj(4) */
+ *sint_ptr = 4;
+ break;
+ case 2: /* tcpRtoMin */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 1000;
+ break;
+ case 3: /* tcpRtoMax */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 60000;
+ break;
+ case 4: /* tcpMaxConn */
+ *sint_ptr = MEMP_NUM_TCP_PCB;
+ break;
+ case 5: /* tcpActiveOpens */
+ *uint_ptr = tcpactiveopens;
+ break;
+ case 6: /* tcpPassiveOpens */
+ *uint_ptr = tcppassiveopens;
+ break;
+ case 7: /* tcpAttemptFails */
+ *uint_ptr = tcpattemptfails;
+ break;
+ case 8: /* tcpEstabResets */
+ *uint_ptr = tcpestabresets;
+ break;
+ case 9: /* tcpCurrEstab */
+ {
+ u16_t tcpcurrestab = 0;
+ struct tcp_pcb *pcb = tcp_active_pcbs;
+ while (pcb != NULL)
+ {
+ if ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))
+ {
+ tcpcurrestab++;
+ }
+ pcb = pcb->next;
+ }
+ *uint_ptr = tcpcurrestab;
+ }
+ break;
+ case 10: /* tcpInSegs */
+ *uint_ptr = tcpinsegs;
+ break;
+ case 11: /* tcpOutSegs */
+ *uint_ptr = tcpoutsegs;
+ break;
+ case 12: /* tcpRetransSegs */
+ *uint_ptr = tcpretranssegs;
+ break;
+ case 14: /* tcpInErrs */
+ *uint_ptr = tcpinerrs;
+ break;
+ case 15: /* tcpOutRsts */
+ *uint_ptr = tcpoutrsts;
+ break;
+ }
+}
+#ifdef THIS_SEEMS_UNUSED
+static void
+tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (10) */
+ ident_len += 10;
+ ident -= 10;
+
+ if (ident_len == 11)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ id = ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
+
+ switch (id)
+ {
+ case 1: /* tcpConnState */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* tcpConnLocalAddress */
+ case 4: /* tcpConnRemAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 3: /* tcpConnLocalPort */
+ case 5: /* tcpConnRemPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ ip_addr_t lip, rip;
+ u16_t lport, rport;
+ s32_t *ident;
+
+ ident = od->id_inst_ptr;
+ snmp_oidtoip(&ident[1], &lip);
+ lport = ident[5];
+ snmp_oidtoip(&ident[6], &rip);
+ rport = ident[10];
+
+ /** @todo find matching PCB */
+}
+#endif /* if 0 */
+#endif
+
+static void
+udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if ((ident_len == 2) &&
+ (ident[0] > 0) && (ident[0] < 6))
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpInDatagrams */
+ *uint_ptr = udpindatagrams;
+ break;
+ case 2: /* udpNoPorts */
+ *uint_ptr = udpnoports;
+ break;
+ case 3: /* udpInErrors */
+ *uint_ptr = udpinerrors;
+ break;
+ case 4: /* udpOutDatagrams */
+ *uint_ptr = udpoutdatagrams;
+ break;
+ }
+}
+
+static void
+udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* udpLocalAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* udpLocalPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udpentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ struct udp_pcb *pcb;
+ ip_addr_t ip;
+ u16_t port;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], &ip);
+ LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff));
+ port = (u16_t)od->id_inst_ptr[5];
+
+ pcb = udp_pcbs;
+ while ((pcb != NULL) &&
+ !(ip_addr_cmp(&pcb->local_ip, &ip) &&
+ (pcb->local_port == port)))
+ {
+ pcb = pcb->next;
+ }
+
+ if (pcb != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpLocalAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = pcb->local_ip;
+ }
+ break;
+ case 2: /* udpLocalPort */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = pcb->local_port;
+ }
+ break;
+ }
+ }
+}
+
+static void
+snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* snmpInPkts */
+ case 2: /* snmpOutPkts */
+ case 3: /* snmpInBadVersions */
+ case 4: /* snmpInBadCommunityNames */
+ case 5: /* snmpInBadCommunityUses */
+ case 6: /* snmpInASNParseErrs */
+ case 8: /* snmpInTooBigs */
+ case 9: /* snmpInNoSuchNames */
+ case 10: /* snmpInBadValues */
+ case 11: /* snmpInReadOnlys */
+ case 12: /* snmpInGenErrs */
+ case 13: /* snmpInTotalReqVars */
+ case 14: /* snmpInTotalSetVars */
+ case 15: /* snmpInGetRequests */
+ case 16: /* snmpInGetNexts */
+ case 17: /* snmpInSetRequests */
+ case 18: /* snmpInGetResponses */
+ case 19: /* snmpInTraps */
+ case 20: /* snmpOutTooBigs */
+ case 21: /* snmpOutNoSuchNames */
+ case 22: /* snmpOutBadValues */
+ case 24: /* snmpOutGenErrs */
+ case 25: /* snmpOutGetRequests */
+ case 26: /* snmpOutGetNexts */
+ case 27: /* snmpOutSetRequests */
+ case 28: /* snmpOutGetResponses */
+ case 29: /* snmpOutTraps */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+snmp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* snmpInPkts */
+ *uint_ptr = snmpinpkts;
+ break;
+ case 2: /* snmpOutPkts */
+ *uint_ptr = snmpoutpkts;
+ break;
+ case 3: /* snmpInBadVersions */
+ *uint_ptr = snmpinbadversions;
+ break;
+ case 4: /* snmpInBadCommunityNames */
+ *uint_ptr = snmpinbadcommunitynames;
+ break;
+ case 5: /* snmpInBadCommunityUses */
+ *uint_ptr = snmpinbadcommunityuses;
+ break;
+ case 6: /* snmpInASNParseErrs */
+ *uint_ptr = snmpinasnparseerrs;
+ break;
+ case 8: /* snmpInTooBigs */
+ *uint_ptr = snmpintoobigs;
+ break;
+ case 9: /* snmpInNoSuchNames */
+ *uint_ptr = snmpinnosuchnames;
+ break;
+ case 10: /* snmpInBadValues */
+ *uint_ptr = snmpinbadvalues;
+ break;
+ case 11: /* snmpInReadOnlys */
+ *uint_ptr = snmpinreadonlys;
+ break;
+ case 12: /* snmpInGenErrs */
+ *uint_ptr = snmpingenerrs;
+ break;
+ case 13: /* snmpInTotalReqVars */
+ *uint_ptr = snmpintotalreqvars;
+ break;
+ case 14: /* snmpInTotalSetVars */
+ *uint_ptr = snmpintotalsetvars;
+ break;
+ case 15: /* snmpInGetRequests */
+ *uint_ptr = snmpingetrequests;
+ break;
+ case 16: /* snmpInGetNexts */
+ *uint_ptr = snmpingetnexts;
+ break;
+ case 17: /* snmpInSetRequests */
+ *uint_ptr = snmpinsetrequests;
+ break;
+ case 18: /* snmpInGetResponses */
+ *uint_ptr = snmpingetresponses;
+ break;
+ case 19: /* snmpInTraps */
+ *uint_ptr = snmpintraps;
+ break;
+ case 20: /* snmpOutTooBigs */
+ *uint_ptr = snmpouttoobigs;
+ break;
+ case 21: /* snmpOutNoSuchNames */
+ *uint_ptr = snmpoutnosuchnames;
+ break;
+ case 22: /* snmpOutBadValues */
+ *uint_ptr = snmpoutbadvalues;
+ break;
+ case 24: /* snmpOutGenErrs */
+ *uint_ptr = snmpoutgenerrs;
+ break;
+ case 25: /* snmpOutGetRequests */
+ *uint_ptr = snmpoutgetrequests;
+ break;
+ case 26: /* snmpOutGetNexts */
+ *uint_ptr = snmpoutgetnexts;
+ break;
+ case 27: /* snmpOutSetRequests */
+ *uint_ptr = snmpoutsetrequests;
+ break;
+ case 28: /* snmpOutGetResponses */
+ *uint_ptr = snmpoutgetresponses;
+ break;
+ case 29: /* snmpOutTraps */
+ *uint_ptr = snmpouttraps;
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ *uint_ptr = *snmpenableauthentraps_ptr;
+ break;
+ };
+}
+
+/**
+ * Test snmp object value before setting.
+ *
+ * @param od is the object definition
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ */
+static u8_t
+snmp_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+
+ LWIP_UNUSED_ARG(len);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ if (id == 30)
+ {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default)
+ {
+ /* we should have writable non-volatile mem here */
+ if ((*sint_ptr == 1) || (*sint_ptr == 2))
+ {
+ set_ok = 1;
+ }
+ }
+ else
+ {
+ /* const or hardwired value */
+ if (*sint_ptr == snmpenableauthentraps_default)
+ {
+ set_ok = 1;
+ }
+ }
+ }
+ return set_ok;
+}
+
+static void
+snmp_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ if (id == 30)
+ {
+ /* snmpEnableAuthenTraps */
+ /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */
+ u8_t *ptr = (u8_t*)value;
+ *snmpenableauthentraps_ptr = *ptr;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/Libraries/Lwip/lwip/src/core/snmp/mib_structs.c b/Libraries/Lwip/lwip/src/core/snmp/mib_structs.c
new file mode 100644
index 00000000..2f185cb4
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/snmp/mib_structs.c
@@ -0,0 +1,1174 @@
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_structs.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+
+/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
+const s32_t prefix[4] = {1, 3, 6, 1};
+
+#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
+/** node stack entry (old news?) */
+struct nse
+{
+ /** right child */
+ struct mib_node* r_ptr;
+ /** right child identifier */
+ s32_t r_id;
+ /** right child next level */
+ u8_t r_nl;
+};
+static u8_t node_stack_cnt;
+static struct nse node_stack[NODE_STACK_SIZE];
+
+/**
+ * Pushes nse struct onto stack.
+ */
+static void
+push_node(struct nse* node)
+{
+ LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id));
+ if (node_stack_cnt < NODE_STACK_SIZE)
+ {
+ node_stack[node_stack_cnt] = *node;
+ node_stack_cnt++;
+ }
+}
+
+/**
+ * Pops nse struct from stack.
+ */
+static void
+pop_node(struct nse* node)
+{
+ if (node_stack_cnt > 0)
+ {
+ node_stack_cnt--;
+ *node = node_stack[node_stack_cnt];
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id));
+}
+
+/**
+ * Conversion from ifIndex to lwIP netif
+ * @param ifindex is a s32_t object sub-identifier
+ * @param netif points to returned netif struct pointer
+ */
+void
+snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
+{
+ struct netif *nif = netif_list;
+ s32_t i, ifidx;
+
+ ifidx = ifindex - 1;
+ i = 0;
+ while ((nif != NULL) && (i < ifidx))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *netif = nif;
+}
+
+/**
+ * Conversion from lwIP netif to ifIndex
+ * @param netif points to a netif struct
+ * @param ifidx points to s32_t object sub-identifier
+ */
+void
+snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
+{
+ struct netif *nif = netif_list;
+ u16_t i;
+
+ i = 0;
+ while ((nif != NULL) && (nif != netif))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *ifidx = i+1;
+}
+
+/**
+ * Conversion from oid to lwIP ip_addr
+ * @param ident points to s32_t ident[4] input
+ * @param ip points to output struct
+ */
+void
+snmp_oidtoip(s32_t *ident, ip_addr_t *ip)
+{
+ IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
+}
+
+/**
+ * Conversion from lwIP ip_addr to oid
+ * @param ip points to input struct
+ * @param ident points to s32_t ident[4] output
+ */
+void
+snmp_iptooid(ip_addr_t *ip, s32_t *ident)
+{
+ ident[0] = ip4_addr1(ip);
+ ident[1] = ip4_addr2(ip);
+ ident[2] = ip4_addr3(ip);
+ ident[3] = ip4_addr4(ip);
+}
+
+struct mib_list_node *
+snmp_mib_ln_alloc(s32_t id)
+{
+ struct mib_list_node *ln;
+
+ ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE);
+ if (ln != NULL)
+ {
+ ln->prev = NULL;
+ ln->next = NULL;
+ ln->objid = id;
+ ln->nptr = NULL;
+ }
+ return ln;
+}
+
+void
+snmp_mib_ln_free(struct mib_list_node *ln)
+{
+ memp_free(MEMP_SNMP_NODE, ln);
+}
+
+struct mib_list_rootnode *
+snmp_mib_lrn_alloc(void)
+{
+ struct mib_list_rootnode *lrn;
+
+ lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE);
+ if (lrn != NULL)
+ {
+ lrn->get_object_def = noleafs_get_object_def;
+ lrn->get_value = noleafs_get_value;
+ lrn->set_test = noleafs_set_test;
+ lrn->set_value = noleafs_set_value;
+ lrn->node_type = MIB_NODE_LR;
+ lrn->maxlength = 0;
+ lrn->head = NULL;
+ lrn->tail = NULL;
+ lrn->count = 0;
+ }
+ return lrn;
+}
+
+void
+snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
+{
+ memp_free(MEMP_SNMP_ROOTNODE, lrn);
+}
+
+/**
+ * Inserts node in idx list in a sorted
+ * (ascending order) fashion and
+ * allocates the node if needed.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param insn points to a pointer to the inserted node
+ * used for constructing the tree.
+ * @return -1 if failed, 1 if inserted, 2 if present.
+ */
+s8_t
+snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
+{
+ struct mib_list_node *nn;
+ s8_t insert;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+
+ /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
+ insert = 0;
+ if (rn->head == NULL)
+ {
+ /* empty list, add first node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ rn->head = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ insert = -1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *n;
+ /* at least one node is present */
+ n = rn->head;
+ while ((n != NULL) && (insert == 0))
+ {
+ if (n->objid == objid)
+ {
+ /* node is already there */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
+ *insn = n;
+ insert = 2;
+ }
+ else if (n->objid < objid)
+ {
+ if (n->next == NULL)
+ {
+ /* alloc and insert at the tail */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ nn->next = NULL;
+ nn->prev = n;
+ n->next = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ else
+ {
+ /* there's more to explore: traverse list */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
+ n = n->next;
+ }
+ }
+ else
+ {
+ /* n->objid > objid */
+ /* alloc and insert between n->prev and n */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ if (n->prev == NULL)
+ {
+ /* insert at the head */
+ nn->next = n;
+ nn->prev = NULL;
+ rn->head = nn;
+ n->prev = nn;
+ }
+ else
+ {
+ /* insert in the middle */
+ nn->next = n;
+ nn->prev = n->prev;
+ n->prev->next = nn;
+ n->prev = nn;
+ }
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ }
+ }
+ if (insert == 1)
+ {
+ rn->count += 1;
+ }
+ LWIP_ASSERT("insert != 0",insert != 0);
+ return insert;
+}
+
+/**
+ * Finds node in idx list and returns deletion mark.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param fn returns pointer to found node
+ * @return 0 if not found, 1 if deletable,
+ * 2 can't delete (2 or more children), 3 not a list_node
+ */
+s8_t
+snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
+{
+ s8_t fc;
+ struct mib_list_node *n;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ n = rn->head;
+ while ((n != NULL) && (n->objid != objid))
+ {
+ n = n->next;
+ }
+ if (n == NULL)
+ {
+ fc = 0;
+ }
+ else if (n->nptr == NULL)
+ {
+ /* leaf, can delete node */
+ fc = 1;
+ }
+ else
+ {
+ struct mib_list_rootnode *r;
+
+ if (n->nptr->node_type == MIB_NODE_LR)
+ {
+ r = (struct mib_list_rootnode *)n->nptr;
+ if (r->count > 1)
+ {
+ /* can't delete node */
+ fc = 2;
+ }
+ else
+ {
+ /* count <= 1, can delete node */
+ fc = 1;
+ }
+ }
+ else
+ {
+ /* other node type */
+ fc = 3;
+ }
+ }
+ *fn = n;
+ return fc;
+}
+
+/**
+ * Removes node from idx list
+ * if it has a single child left.
+ *
+ * @param rn points to the root node
+ * @param n points to the node to delete
+ * @return the nptr to be freed by caller
+ */
+struct mib_list_rootnode *
+snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
+{
+ struct mib_list_rootnode *next;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ LWIP_ASSERT("n != NULL",n != NULL);
+
+ /* caller must remove this sub-tree */
+ next = (struct mib_list_rootnode*)(n->nptr);
+ rn->count -= 1;
+
+ if (n == rn->head)
+ {
+ rn->head = n->next;
+ if (n->next != NULL)
+ {
+ /* not last node, new list begin */
+ n->next->prev = NULL;
+ }
+ }
+ else if (n == rn->tail)
+ {
+ rn->tail = n->prev;
+ if (n->prev != NULL)
+ {
+ /* not last node, new list end */
+ n->prev->next = NULL;
+ }
+ }
+ else
+ {
+ /* node must be in the middle */
+ n->prev->next = n->next;
+ n->next->prev = n->prev;
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
+ snmp_mib_ln_free(n);
+ if (rn->count == 0)
+ {
+ rn->head = NULL;
+ rn->tail = NULL;
+ }
+ return next;
+}
+
+
+
+/**
+ * Searches tree for the supplied (scalar?) object identifier.
+ *
+ * @param node points to the root of the tree ('.internet')
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param np points to the found object instance (return)
+ * @return pointer to the requested parent (!) node if success, NULL otherwise
+ */
+struct mib_node *
+snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np)
+{
+ u8_t node_type, ext_level;
+
+ ext_level = 0;
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident));
+ while (node != NULL)
+ {
+ node_type = node->node_type;
+ if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ u16_t i;
+
+ if (ident_len > 0)
+ {
+ /* array node (internal ROM or RAM, fixed length) */
+ an = (struct mib_array_node *)node;
+ i = 0;
+ while ((i < an->maxlength) && (an->objid[i] != *ident))
+ {
+ i++;
+ }
+ if (i < an->maxlength)
+ {
+ /* found it, if available proceed to child, otherwise inspect leaf */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
+ if (an->nptr[i] == NULL)
+ {
+ /* a scalar leaf OR table,
+ inspect remaining instance number / table index */
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* follow next child pointer */
+ ident++;
+ ident_len--;
+ node = an->nptr[i];
+ }
+ }
+ else
+ {
+ /* search failed, identifier mismatch (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if(node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ struct mib_list_node *ln;
+
+ if (ident_len > 0)
+ {
+ /* list root node (internal 'RAM', variable length) */
+ lrn = (struct mib_list_rootnode *)node;
+ ln = lrn->head;
+ /* iterate over list, head to tail */
+ while ((ln != NULL) && (ln->objid != *ident))
+ {
+ ln = ln->next;
+ }
+ if (ln != NULL)
+ {
+ /* found it, proceed to child */;
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
+ if (ln->nptr == NULL)
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* follow next child pointer */
+ ident_len--;
+ ident++;
+ node = ln->nptr;
+ }
+ }
+ else
+ {
+ /* search failed */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if(node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ u16_t i, len;
+
+ if (ident_len > 0)
+ {
+ /* external node (addressing and access via functions) */
+ en = (struct mib_external_node *)node;
+
+ i = 0;
+ len = en->level_length(en->addr_inf,ext_level);
+ while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0))
+ {
+ i++;
+ }
+ if (i < len)
+ {
+ s32_t debug_id;
+
+ en->get_objid(en->addr_inf,ext_level,i,&debug_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident));
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* found it, proceed to child */
+ ident_len--;
+ ident++;
+ ext_level++;
+ }
+ }
+ else
+ {
+ /* search failed */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if (node_type == MIB_NODE_SC)
+ {
+ mib_scalar_node *sn;
+
+ sn = (mib_scalar_node *)node;
+ if ((ident_len == 1) && (*ident == 0))
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)sn;
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n"));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* unknown node_type */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type));
+ return NULL;
+ }
+ }
+ /* done, found nothing */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node));
+ return NULL;
+}
+
+/**
+ * Test table for presence of at least one table entry.
+ */
+static u8_t
+empty_table(struct mib_node *node)
+{
+ u8_t node_type;
+ u8_t empty = 0;
+
+ if (node != NULL)
+ {
+ node_type = node->node_type;
+ if (node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ lrn = (struct mib_list_rootnode *)node;
+ if ((lrn->count == 0) || (lrn->head == NULL))
+ {
+ empty = 1;
+ }
+ }
+ else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ an = (struct mib_array_node *)node;
+ if ((an->maxlength == 0) || (an->nptr == NULL))
+ {
+ empty = 1;
+ }
+ }
+ else if (node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ en = (struct mib_external_node *)node;
+ if (en->tree_levels == 0)
+ {
+ empty = 1;
+ }
+ }
+ }
+ return empty;
+}
+
+/**
+ * Tree expansion.
+ */
+struct mib_node *
+snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
+{
+ u8_t node_type, ext_level, climb_tree;
+
+ ext_level = 0;
+ /* reset node stack */
+ node_stack_cnt = 0;
+ while (node != NULL)
+ {
+ climb_tree = 0;
+ node_type = node->node_type;
+ if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ u16_t i;
+
+ /* array node (internal ROM or RAM, fixed length) */
+ an = (struct mib_array_node *)node;
+ if (ident_len > 0)
+ {
+ i = 0;
+ while ((i < an->maxlength) && (an->objid[i] < *ident))
+ {
+ i++;
+ }
+ if (i < an->maxlength)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
+ /* add identifier to oidret */
+ oidret->id[oidret->len] = an->objid[i];
+ (oidret->len)++;
+
+ if (an->nptr[i] == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
+ /* leaf node (e.g. in a fixed size table) */
+ if (an->objid[i] > *ident)
+ {
+ return (struct mib_node*)an;
+ }
+ else if ((i + 1) < an->maxlength)
+ {
+ /* an->objid[i] == *ident */
+ (oidret->len)--;
+ oidret->id[oidret->len] = an->objid[i + 1];
+ (oidret->len)++;
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* (i + 1) == an->maxlength */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ struct nse cur_node;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = (u8_t)i + 1;
+ while ((j < an->maxlength) && (empty_table(an->nptr[j])))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ cur_node.r_ptr = an->nptr[j];
+ cur_node.r_id = an->objid[j];
+ cur_node.r_nl = 0;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (an->objid[i] == *ident)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* an->objid[i] < *ident */
+ ident_len = 0;
+ }
+ /* follow next child pointer */
+ node = an->nptr[i];
+ }
+ }
+ else
+ {
+ /* i == an->maxlength */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ /* ident_len == 0, complete with leftmost '.thing' */
+ j = 0;
+ while ((j < an->maxlength) && empty_table(an->nptr[j]))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j]));
+ oidret->id[oidret->len] = an->objid[j];
+ (oidret->len)++;
+ if (an->nptr[j] == NULL)
+ {
+ /* leaf node */
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* no leaf, continue */
+ node = an->nptr[j];
+ }
+ }
+ else
+ {
+ /* j == an->maxlength */
+ climb_tree = 1;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ struct mib_list_node *ln;
+
+ /* list root node (internal 'RAM', variable length) */
+ lrn = (struct mib_list_rootnode *)node;
+ if (ident_len > 0)
+ {
+ ln = lrn->head;
+ /* iterate over list, head to tail */
+ while ((ln != NULL) && (ln->objid < *ident))
+ {
+ ln = ln->next;
+ }
+ if (ln != NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
+ oidret->id[oidret->len] = ln->objid;
+ (oidret->len)++;
+ if (ln->nptr == NULL)
+ {
+ /* leaf node */
+ if (ln->objid > *ident)
+ {
+ return (struct mib_node*)lrn;
+ }
+ else if (ln->next != NULL)
+ {
+ /* ln->objid == *ident */
+ (oidret->len)--;
+ oidret->id[oidret->len] = ln->next->objid;
+ (oidret->len)++;
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* ln->next == NULL */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *jn;
+ struct nse cur_node;
+
+ /* non-leaf, store right child ptr and id */
+ jn = ln->next;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ cur_node.r_ptr = jn->nptr;
+ cur_node.r_id = jn->objid;
+ cur_node.r_nl = 0;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (ln->objid == *ident)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* ln->objid < *ident */
+ ident_len = 0;
+ }
+ /* follow next child pointer */
+ node = ln->nptr;
+ }
+
+ }
+ else
+ {
+ /* ln == NULL */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *jn;
+ /* ident_len == 0, complete with leftmost '.thing' */
+ jn = lrn->head;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid));
+ oidret->id[oidret->len] = jn->objid;
+ (oidret->len)++;
+ if (jn->nptr == NULL)
+ {
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n"));
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* no leaf, continue */
+ node = jn->nptr;
+ }
+ }
+ else
+ {
+ /* jn == NULL */
+ climb_tree = 1;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ s32_t ex_id;
+
+ /* external node (addressing and access via functions) */
+ en = (struct mib_external_node *)node;
+ if (ident_len > 0)
+ {
+ u16_t i, len;
+
+ i = 0;
+ len = en->level_length(en->addr_inf,ext_level);
+ while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0))
+ {
+ i++;
+ }
+ if (i < len)
+ {
+ /* add identifier to oidret */
+ en->get_objid(en->addr_inf,ext_level,i,&ex_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident));
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
+ /* leaf node */
+ if (ex_id > *ident)
+ {
+ return (struct mib_node*)en;
+ }
+ else if ((i + 1) < len)
+ {
+ /* ex_id == *ident */
+ en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id);
+ (oidret->len)--;
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* (i + 1) == len */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ struct nse cur_node;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = (u8_t)i + 1;
+ if (j < len)
+ {
+ /* right node is the current external node */
+ cur_node.r_ptr = node;
+ en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id);
+ cur_node.r_nl = ext_level + 1;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* external id < *ident */
+ ident_len = 0;
+ }
+ /* proceed to child */
+ ext_level++;
+ }
+ }
+ else
+ {
+ /* i == len (en->level_len()) */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ /* ident_len == 0, complete with leftmost '.thing' */
+ en->get_objid(en->addr_inf,ext_level,0,&ex_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id));
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n"));
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* no leaf, proceed to child */
+ ext_level++;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_SC)
+ {
+ mib_scalar_node *sn;
+
+ /* scalar node */
+ sn = (mib_scalar_node *)node;
+ if (ident_len > 0)
+ {
+ /* at .0 */
+ climb_tree = 1;
+ }
+ else
+ {
+ /* ident_len == 0, complete object identifier */
+ oidret->id[oidret->len] = 0;
+ (oidret->len)++;
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n"));
+ return (struct mib_node*)sn;
+ }
+ }
+ else
+ {
+ /* unknown/unhandled node_type */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type));
+ return NULL;
+ }
+
+ if (climb_tree)
+ {
+ struct nse child;
+
+ /* find right child ptr */
+ child.r_ptr = NULL;
+ child.r_id = 0;
+ child.r_nl = 0;
+ while ((node_stack_cnt > 0) && (child.r_ptr == NULL))
+ {
+ pop_node(&child);
+ /* trim returned oid */
+ (oidret->len)--;
+ }
+ if (child.r_ptr != NULL)
+ {
+ /* incoming ident is useless beyond this point */
+ ident_len = 0;
+ oidret->id[oidret->len] = child.r_id;
+ oidret->len++;
+ node = child.r_ptr;
+ ext_level = child.r_nl;
+ }
+ else
+ {
+ /* tree ends here ... */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n"));
+ return NULL;
+ }
+ }
+ }
+ /* done, found nothing */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node));
+ return NULL;
+}
+
+/**
+ * Test object identifier for the iso.org.dod.internet prefix.
+ *
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @return 1 if it matches, 0 otherwise
+ */
+u8_t
+snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident)
+{
+ if ((ident_len > 3) &&
+ (ident[0] == 1) && (ident[1] == 3) &&
+ (ident[2] == 6) && (ident[3] == 1))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * Expands object identifier to the iso.org.dod.internet
+ * prefix for use in getnext operation.
+ *
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param oidret points to returned expanded object identifier
+ * @return 1 if it matches, 0 otherwise
+ *
+ * @note ident_len 0 is allowed, expanding to the first known object id!!
+ */
+u8_t
+snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
+{
+ const s32_t *prefix_ptr;
+ s32_t *ret_ptr;
+ u8_t i;
+
+ i = 0;
+ prefix_ptr = &prefix[0];
+ ret_ptr = &oidret->id[0];
+ ident_len = ((ident_len < 4)?ident_len:4);
+ while ((i < ident_len) && ((*ident) <= (*prefix_ptr)))
+ {
+ *ret_ptr++ = *prefix_ptr++;
+ ident++;
+ i++;
+ }
+ if (i == ident_len)
+ {
+ /* match, complete missing bits */
+ while (i < 4)
+ {
+ *ret_ptr++ = *prefix_ptr++;
+ i++;
+ }
+ oidret->len = i;
+ return 1;
+ }
+ else
+ {
+ /* i != ident_len */
+ return 0;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/Libraries/Lwip/lwip/src/core/snmp/msg_in.c b/Libraries/Lwip/lwip/src/core/snmp/msg_in.c
new file mode 100644
index 00000000..be940c62
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/snmp/msg_in.c
@@ -0,0 +1,1453 @@
+/**
+ * @file
+ * SNMP input message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/ip_addr.h"
+#include "lwip/memp.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+/* public (non-static) constants */
+/** SNMP v1 == 0 */
+const s32_t snmp_version = 0;
+/** default SNMP community string */
+const char snmp_publiccommunity[7] = "public";
+
+/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
+struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
+/* UDP Protocol Control Block */
+struct udp_pcb *snmp1_pcb;
+
+static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
+static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
+
+
+/**
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
+ */
+void
+snmp_init(void)
+{
+ struct snmp_msg_pstat *msg_ps;
+ u8_t i;
+
+ snmp1_pcb = udp_new();
+ if (snmp1_pcb != NULL)
+ {
+ udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
+ udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
+ }
+ msg_ps = &msg_input_list[0];
+ for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
+ {
+ msg_ps->state = SNMP_MSG_EMPTY;
+ msg_ps->error_index = 0;
+ msg_ps->error_status = SNMP_ES_NOERROR;
+ msg_ps++;
+ }
+ trap_msg.pcb = snmp1_pcb;
+
+#ifdef SNMP_PRIVATE_MIB_INIT
+ /* If defined, 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; v<msg_ps->vb_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/Libraries/Lwip/lwip/src/core/snmp/msg_out.c b/Libraries/Lwip/lwip/src/core/snmp/msg_out.c
new file mode 100644
index 00000000..485f076a
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/core/snmp/msg_out.c
@@ -0,0 +1,674 @@
+/**
+ * @file
+ * SNMP output message processing (RFC1157).
+ *
+ * Output responses and traps are build in two passes:
+ *
+ * Pass 0: iterate over the output message backwards to determine encoding lengths
+ * Pass 1: the actual forward encoding of internal form into ASN1
+ *
+ * The single-pass encoding method described by Comer & Stevens
+ * requires extra buffer space and copying for reversal of the packet.
+ * The buffer requirement can be prohibitively large for big payloads
+ * (>= 484) therefore we use the two encoding passes.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_msg.h"
+
+struct snmp_trap_dst
+{
+ /* destination IP address in network order */
+ ip_addr_t dip;
+ /* set to 0 when disabled, >0 when enabled */
+ u8_t enable;
+};
+struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
+
+/** TRAP message structure */
+struct snmp_msg_trap trap_msg;
+
+static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len);
+static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len);
+static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root);
+
+static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p);
+static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p);
+static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs);
+
+/**
+ * Sets enable switch for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param enable switch if 0 destination is disabled >0 enabled.
+ */
+void
+snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
+{
+ if (dst_idx < SNMP_TRAP_DESTINATIONS)
+ {
+ trap_dst[dst_idx].enable = enable;
+ }
+}
+
+/**
+ * Sets IPv4 address for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param dst IPv4 address in host order.
+ */
+void
+snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst)
+{
+ if (dst_idx < SNMP_TRAP_DESTINATIONS)
+ {
+ ip_addr_set(&trap_dst[dst_idx].dip, dst);
+ }
+}
+
+/**
+ * Sends a 'getresponse' message to the request originator.
+ *
+ * @param m_stat points to the current message request state source
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the caller is responsible for filling in outvb in the m_stat
+ * and provide error-status and index (except for tooBig errors) ...
+ */
+err_t
+snmp_send_response(struct snmp_msg_pstat *m_stat)
+{
+ struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0};
+ struct pbuf *p;
+ u16_t tot_len;
+ err_t err;
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_varbind_list_sum(&m_stat->outvb);
+ tot_len = snmp_resp_header_sum(m_stat, tot_len);
+
+ /* try allocating pbuf(s) for complete response */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ if (p == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n"));
+
+ /* can't construct reply, return error-status tooBig */
+ m_stat->error_status = SNMP_ES_TOOBIG;
+ m_stat->error_index = 0;
+ /* pass 0, recalculate lengths, for empty varbind-list */
+ tot_len = snmp_varbind_list_sum(&emptyvb);
+ tot_len = snmp_resp_header_sum(m_stat, tot_len);
+ /* retry allocation once for header and empty varbind-list */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ }
+ if (p != NULL)
+ {
+ /* first pbuf alloc try or retry alloc success */
+ u16_t ofs;
+
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n"));
+
+ /* pass 1, size error, encode packet ino the pbuf(s) */
+ ofs = snmp_resp_header_enc(m_stat, p);
+ snmp_varbind_list_enc(&m_stat->outvb, p, ofs);
+
+ switch (m_stat->error_status)
+ {
+ case SNMP_ES_TOOBIG:
+ snmp_inc_snmpouttoobigs();
+ break;
+ case SNMP_ES_NOSUCHNAME:
+ snmp_inc_snmpoutnosuchnames();
+ break;
+ case SNMP_ES_BADVALUE:
+ snmp_inc_snmpoutbadvalues();
+ break;
+ case SNMP_ES_GENERROR:
+ snmp_inc_snmpoutgenerrs();
+ break;
+ }
+ snmp_inc_snmpoutgetresponses();
+ snmp_inc_snmpoutpkts();
+
+ /** @todo do we need separate rx and tx pcbs for threaded case? */
+ /** connect to the originating source */
+ udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp);
+ err = udp_send(m_stat->pcb, p);
+ if (err == ERR_MEM)
+ {
+ /** @todo release some memory, retry and return tooBig? tooMuchHassle? */
+ err = ERR_MEM;
+ }
+ else
+ {
+ err = ERR_OK;
+ }
+ /** disassociate remote address and port with this pcb */
+ udp_disconnect(m_stat->pcb);
+
+ pbuf_free(p);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n"));
+ return err;
+ }
+ else
+ {
+ /* first pbuf alloc try or retry alloc failed
+ very low on memory, couldn't return tooBig */
+ return ERR_MEM;
+ }
+}
+
+
+/**
+ * Sends an generic or enterprise specific trap message.
+ *
+ * @param generic_trap is the trap code
+ * @param eoid points to enterprise object identifier
+ * @param specific_trap used for enterprise traps when generic_trap == 6
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the caller is responsible for filling in outvb in the trap_msg
+ * @note the use of the enterpise identifier field
+ * is per RFC1215.
+ * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
+ * and .iso.org.dod.internet.private.enterprises.yourenterprise
+ * (sysObjectID) for specific traps.
+ */
+err_t
+snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap)
+{
+ struct snmp_trap_dst *td;
+ struct netif *dst_if;
+ ip_addr_t dst_ip;
+ struct pbuf *p;
+ u16_t i,tot_len;
+
+ for (i=0, td = &trap_dst[0]; i<SNMP_TRAP_DESTINATIONS; i++, td++)
+ {
+ if ((td->enable != 0) && !ip_addr_isany(&td->dip))
+ {
+ /* network order trap destination */
+ ip_addr_copy(trap_msg.dip, td->dip);
+ /* lookup current source address for this dst */
+ dst_if = ip_route(&td->dip);
+ ip_addr_copy(dst_ip, dst_if->ip_addr);
+ /* @todo: what about IPv6? */
+ trap_msg.sip_raw[0] = ip4_addr1(&dst_ip);
+ trap_msg.sip_raw[1] = ip4_addr2(&dst_ip);
+ trap_msg.sip_raw[2] = ip4_addr3(&dst_ip);
+ trap_msg.sip_raw[3] = ip4_addr4(&dst_ip);
+ trap_msg.gen_trap = generic_trap;
+ trap_msg.spc_trap = specific_trap;
+ if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC)
+ {
+ /* enterprise-Specific trap */
+ trap_msg.enterprise = eoid;
+ }
+ else
+ {
+ /* generic (MIB-II) trap */
+ snmp_get_snmpgrpid_ptr(&trap_msg.enterprise);
+ }
+ snmp_get_sysuptime(&trap_msg.ts);
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_varbind_list_sum(&trap_msg.outvb);
+ tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+ /* allocate pbuf(s) */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ if (p != NULL)
+ {
+ u16_t ofs;
+
+ /* pass 1, encode packet ino the pbuf(s) */
+ ofs = snmp_trap_header_enc(&trap_msg, p);
+ snmp_varbind_list_enc(&trap_msg.outvb, p, ofs);
+
+ snmp_inc_snmpouttraps();
+ snmp_inc_snmpoutpkts();
+
+ /** send to the TRAP destination */
+ udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT);
+
+ pbuf_free(p);
+ }
+ else
+ {
+ return ERR_MEM;
+ }
+ }
+ }
+ return ERR_OK;
+}
+
+void
+snmp_coldstart_trap(void)
+{
+ trap_msg.outvb.head = NULL;
+ trap_msg.outvb.tail = NULL;
+ trap_msg.outvb.count = 0;
+ snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0);
+}
+
+void
+snmp_authfail_trap(void)
+{
+ u8_t enable;
+ snmp_get_snmpenableauthentraps(&enable);
+ if (enable == 1)
+ {
+ trap_msg.outvb.head = NULL;
+ trap_msg.outvb.tail = NULL;
+ trap_msg.outvb.count = 0;
+ snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0);
+ }
+}
+
+/**
+ * Sums response header field lengths from tail to head and
+ * returns resp_header_lengths for second encoding pass.
+ *
+ * @param vb_len varbind-list length
+ * @param rhl points to returned header lengths
+ * @return the required lenght for encoding the response header
+ */
+static u16_t
+snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len)
+{
+ u16_t tot_len;
+ struct snmp_resp_header_lengths *rhl;
+
+ rhl = &m_stat->rhl;
+ tot_len = vb_len;
+ snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen);
+ snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen);
+ tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen;
+
+ snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen);
+ snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen);
+ tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen;
+
+ snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen);
+ snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen);
+ tot_len += 1 + rhl->ridlenlen + rhl->ridlen;
+
+ rhl->pdulen = tot_len;
+ snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen);
+ tot_len += 1 + rhl->pdulenlen;
+
+ rhl->comlen = m_stat->com_strlen;
+ snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen);
+ tot_len += 1 + rhl->comlenlen + rhl->comlen;
+
+ snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen);
+ snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen);
+ tot_len += 1 + rhl->verlen + rhl->verlenlen;
+
+ rhl->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen);
+ tot_len += 1 + rhl->seqlenlen;
+
+ return tot_len;
+}
+
+/**
+ * Sums trap header field lengths from tail to head and
+ * returns trap_header_lengths for second encoding pass.
+ *
+ * @param vb_len varbind-list length
+ * @param thl points to returned header lengths
+ * @return the required lenght for encoding the trap header
+ */
+static u16_t
+snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len)
+{
+ u16_t tot_len;
+ struct snmp_trap_header_lengths *thl;
+
+ thl = &m_trap->thl;
+ tot_len = vb_len;
+
+ snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen);
+ snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen);
+ tot_len += 1 + thl->tslen + thl->tslenlen;
+
+ snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen);
+ snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen);
+ tot_len += 1 + thl->strplen + thl->strplenlen;
+
+ snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen);
+ snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen);
+ tot_len += 1 + thl->gtrplen + thl->gtrplenlen;
+
+ thl->aaddrlen = 4;
+ snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen);
+ tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen;
+
+ snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen);
+ snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen);
+ tot_len += 1 + thl->eidlen + thl->eidlenlen;
+
+ thl->pdulen = tot_len;
+ snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen);
+ tot_len += 1 + thl->pdulenlen;
+
+ thl->comlen = sizeof(snmp_publiccommunity) - 1;
+ snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen);
+ tot_len += 1 + thl->comlenlen + thl->comlen;
+
+ snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen);
+ snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen);
+ tot_len += 1 + thl->verlen + thl->verlenlen;
+
+ thl->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen);
+ tot_len += 1 + thl->seqlenlen;
+
+ return tot_len;
+}
+
+/**
+ * Sums varbind lengths from tail to head and
+ * annotates lengths in varbind for second encoding pass.
+ *
+ * @param root points to the root of the variable binding list
+ * @return the required lenght for encoding the variable bindings
+ */
+static u16_t
+snmp_varbind_list_sum(struct snmp_varbind_root *root)
+{
+ struct snmp_varbind *vb;
+ u32_t *uint_ptr;
+ s32_t *sint_ptr;
+ u16_t tot_len;
+
+ tot_len = 0;
+ vb = root->tail;
+ while ( vb != NULL )
+ {
+ /* encoded value lenght depends on type */
+ switch (vb->value_type)
+ {
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen);
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
+ uint_ptr = (u32_t*)vb->value;
+ snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen);
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
+ vb->vlen = vb->value_len;
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen);
+ break;
+ default:
+ /* unsupported type */
+ vb->vlen = 0;
+ break;
+ };
+ /* encoding length of value length field */
+ snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen);
+ snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen);
+ snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen);
+
+ vb->seqlen = 1 + vb->vlenlen + vb->vlen;
+ vb->seqlen += 1 + vb->olenlen + vb->olen;
+ snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen);
+
+ /* varbind seq */
+ tot_len += 1 + vb->seqlenlen + vb->seqlen;
+
+ vb = vb->prev;
+ }
+
+ /* varbind-list seq */
+ root->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen);
+ tot_len += 1 + root->seqlenlen;
+
+ return tot_len;
+}
+
+/**
+ * Encodes response header from head to tail.
+ */
+static u16_t
+snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p)
+{
+ u16_t ofs;
+
+ ofs = 0;
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen);
+ ofs += m_stat->rhl.seqlenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen);
+ ofs += m_stat->rhl.verlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version);
+ ofs += m_stat->rhl.verlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen);
+ ofs += m_stat->rhl.comlenlen;
+ snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community);
+ ofs += m_stat->rhl.comlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen);
+ ofs += m_stat->rhl.pdulenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen);
+ ofs += m_stat->rhl.ridlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid);
+ ofs += m_stat->rhl.ridlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen);
+ ofs += m_stat->rhl.errstatlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status);
+ ofs += m_stat->rhl.errstatlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen);
+ ofs += m_stat->rhl.erridxlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index);
+ ofs += m_stat->rhl.erridxlen;
+
+ return ofs;
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static u16_t
+snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p)
+{
+ u16_t ofs;
+
+ ofs = 0;
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen);
+ ofs += m_trap->thl.seqlenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen);
+ ofs += m_trap->thl.verlenlen;
+ snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version);
+ ofs += m_trap->thl.verlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen);
+ ofs += m_trap->thl.comlenlen;
+ snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]);
+ ofs += m_trap->thl.comlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen);
+ ofs += m_trap->thl.pdulenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen);
+ ofs += m_trap->thl.eidlenlen;
+ snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]);
+ ofs += m_trap->thl.eidlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen);
+ ofs += m_trap->thl.aaddrlenlen;
+ snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]);
+ ofs += m_trap->thl.aaddrlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen);
+ ofs += m_trap->thl.gtrplenlen;
+ snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap);
+ ofs += m_trap->thl.gtrplen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen);
+ ofs += m_trap->thl.strplenlen;
+ snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap);
+ ofs += m_trap->thl.strplen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen);
+ ofs += m_trap->thl.tslenlen;
+ snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts);
+ ofs += m_trap->thl.tslen;
+
+ return ofs;
+}
+
+/**
+ * Encodes varbind list from head to tail.
+ */
+static u16_t
+snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs)
+{
+ struct snmp_varbind *vb;
+ s32_t *sint_ptr;
+ u32_t *uint_ptr;
+ u8_t *raw_ptr;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, root->seqlen);
+ ofs += root->seqlenlen;
+
+ vb = root->head;
+ while ( vb != NULL )
+ {
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, vb->seqlen);
+ ofs += vb->seqlenlen;
+
+ snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, vb->olen);
+ ofs += vb->olenlen;
+ snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]);
+ ofs += vb->olen;
+
+ snmp_asn1_enc_type(p, ofs, vb->value_type);
+ ofs += 1;
+ snmp_asn1_enc_length(p, ofs, vb->vlen);
+ ofs += vb->vlenlen;
+
+ switch (vb->value_type)
+ {
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr);
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
+ uint_ptr = (u32_t*)vb->value;
+ snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr);
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
+ raw_ptr = (u8_t*)vb->value;
+ snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr);
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
+ sint_ptr = (s32_t*)vb->value;
+ snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr);
+ break;
+ default:
+ /* unsupported type */
+ break;
+ };
+ ofs += vb->vlen;
+ vb = vb->next;
+ }
+ return ofs;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/Libraries/Lwip/lwip/src/core/stats.c b/Libraries/Lwip/lwip/src/core/stats.c
index 0b3fbc3a..2c5d7937 100644
--- a/Libraries/Lwip/lwip/src/core/stats.c
+++ b/Libraries/Lwip/lwip/src/core/stats.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * 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,
@@ -17,21 +17,21 @@
* 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.
+ * 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
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -50,7 +50,7 @@ struct stats_ lwip_stats;
void stats_init(void)
{
-#ifdef LWIP_STATS
+#ifdef LWIP_DEBUG
#if MEMP_STATS
const char * memp_names[] = {
#define LWIP_MEMPOOL(name,num,size,desc) desc,
@@ -69,21 +69,21 @@ void stats_init(void)
#if LWIP_STATS_DISPLAY
void
-stats_display_proto(struct stats_proto *proto, char *name)
+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));
+ 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
@@ -91,31 +91,31 @@ void
stats_display_igmp(struct stats_igmp *igmp)
{
LWIP_PLATFORM_DIAG(("\nIGMP\n\t"));
- LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
- LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
- LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
- LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
- LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
- LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
- LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
- LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+ LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group));
LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general));
- LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
- LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
- LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
- LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report));
+ LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+ LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+ LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+ LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report));
}
#endif /* IGMP_STATS */
#if MEM_STATS || MEMP_STATS
void
-stats_display_mem(struct stats_mem *mem, char *name)
+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(("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));
}
@@ -139,15 +139,15 @@ void
stats_display_sys(struct stats_sys *sys)
{
LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
- LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
- LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
- LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
- LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
- LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
- LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
- LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
- LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
- LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err));
+ 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 */
diff --git a/Libraries/Lwip/lwip/src/core/sys.c b/Libraries/Lwip/lwip/src/core/sys.c
index 94ff2762..21884600 100644
--- a/Libraries/Lwip/lwip/src/core/sys.c
+++ b/Libraries/Lwip/lwip/src/core/sys.c
@@ -45,6 +45,7 @@
#if !NO_SYS
+#ifndef sys_msleep
/**
* Sleep for some ms. Timeouts are NOT processed while sleeping.
*
@@ -62,5 +63,6 @@ sys_msleep(u32_t ms)
}
}
}
+#endif /* sys_msleep */
#endif /* !NO_SYS */
diff --git a/Libraries/Lwip/lwip/src/core/tcp.c b/Libraries/Lwip/lwip/src/core/tcp.c
index 06fe06cf..3ee2d800 100644
--- a/Libraries/Lwip/lwip/src/core/tcp.c
+++ b/Libraries/Lwip/lwip/src/core/tcp.c
@@ -2,7 +2,7 @@
* @file
* Transmission Control Protocol for IP
*
- * This file contains common functions for the TCP implementation, such as functinos
+ * 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.
*
@@ -42,12 +42,8 @@
#include "lwip/src/include/lwip/opt.h"
-//#pragma message("Testing LWIP_TCP")
-
#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-//#pragma message("LWIP_TCP is true")
-
#include "lwip/src/include/lwip/def.h"
#include "lwip/src/include/lwip/mem.h"
#include "lwip/src/include/lwip/memp.h"
@@ -59,6 +55,22 @@
#include <string.h>
+#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",
@@ -73,6 +85,9 @@ const char * const tcp_state_str[] = {
"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] =
@@ -101,13 +116,26 @@ struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcb
/** 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
+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
tcp_tmr(void)
@@ -143,7 +171,7 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
{
err_t err;
- if (rst_on_unacked_data && (pcb->state != LISTEN)) {
+ if (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. */
@@ -155,14 +183,15 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
pcb->local_port, pcb->remote_port);
tcp_pcb_purge(pcb);
-
- /* TODO: to which state do we move now? */
-
- /* move to TIME_WAIT since we close actively */
- TCP_RMV(&tcp_active_pcbs, pcb);
- pcb->state = TIME_WAIT;
- TCP_REG(&tcp_tw_pcbs, pcb);
-
+ 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;
}
}
@@ -191,7 +220,7 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
break;
case SYN_SENT:
err = ERR_OK;
- tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ TCP_PCB_REMOVE_ACTIVE(pcb);
memp_free(MEMP_TCP_PCB, pcb);
pcb = NULL;
snmp_inc_tcpattemptfails();
@@ -270,7 +299,9 @@ tcp_close(struct tcp_pcb *pcb)
/**
* Causes all or part of a full-duplex connection of this PCB to be shut down.
- * This doesn't deallocate the PCB!
+ * 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
@@ -285,28 +316,32 @@ tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
return ERR_CONN;
}
if (shut_rx) {
- /* shut down the receive side: free buffered data... */
+ /* 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;
}
- /* ... and set a flag not to receive any more data */
- pcb->flags |= TF_RXCLOSED;
}
if (shut_tx) {
/* This can't happen twice since if it succeeds, the pcb's state is changed.
Only close in these states as the others directly deallocate the PCB */
switch (pcb->state) {
- case SYN_RCVD:
- case ESTABLISHED:
- case CLOSE_WAIT:
- return tcp_close_shutdown(pcb, 0);
- default:
- /* don't shut down other states */
- break;
+ 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;
}
}
- /* @todo: return another err_t if not in correct state or already shut? */
return ERR_OK;
}
@@ -322,8 +357,6 @@ void
tcp_abandon(struct tcp_pcb *pcb, int reset)
{
u32_t seqno, ackno;
- u16_t remote_port, local_port;
- ip_addr_t remote_ip, local_ip;
#if LWIP_CALLBACK_API
tcp_err_fn errf;
#endif /* LWIP_CALLBACK_API */
@@ -341,15 +374,11 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
} else {
seqno = pcb->snd_nxt;
ackno = pcb->rcv_nxt;
- ip_addr_copy(local_ip, pcb->local_ip);
- ip_addr_copy(remote_ip, pcb->remote_ip);
- local_port = pcb->local_port;
- remote_port = pcb->remote_port;
#if LWIP_CALLBACK_API
errf = pcb->errf;
#endif /* LWIP_CALLBACK_API */
errf_arg = pcb->callback_arg;
- tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ TCP_PCB_REMOVE_ACTIVE(pcb);
if (pcb->unacked != NULL) {
tcp_segs_free(pcb->unacked);
}
@@ -361,12 +390,12 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
tcp_segs_free(pcb->ooseq);
}
#endif /* TCP_QUEUE_OOSEQ */
- memp_free(MEMP_TCP_PCB, pcb);
- TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
if (reset) {
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
- tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port);
}
+ memp_free(MEMP_TCP_PCB, pcb);
+ TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
}
}
@@ -415,13 +444,16 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
We do not dump TIME_WAIT pcb's; they can still be matched by incoming
packets using both local and remote IP addresses and ports to distinguish.
*/
- if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ 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) */
@@ -432,8 +464,8 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
/* Omit checking for the same port if both pcbs have REUSEADDR set.
For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
tcp_connect. */
- if (((pcb->so_options & SOF_REUSEADDR) == 0) ||
- ((cpcb->so_options & SOF_REUSEADDR) == 0))
+ if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+ !ip_get_option(cpcb, SOF_REUSEADDR))
#endif /* SO_REUSE */
{
if (ip_addr_isany(&(cpcb->local_ip)) ||
@@ -496,7 +528,7 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
return pcb;
}
#if SO_REUSE
- if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ 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. */
@@ -519,7 +551,7 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
lpcb->state = LISTEN;
lpcb->prio = pcb->prio;
lpcb->so_options = pcb->so_options;
- lpcb->so_options |= SOF_ACCEPTCONN;
+ ip_set_option(lpcb, SOF_ACCEPTCONN);
lpcb->ttl = pcb->ttl;
lpcb->tos = pcb->tos;
ip_addr_copy(lpcb->local_ip, pcb->local_ip);
@@ -580,6 +612,9 @@ 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 );
@@ -604,37 +639,33 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len)
}
/**
- * A nastly hack featuring 'goto' statements that allocates a
- * new TCP local port.
+ * Allocate a new local TCP port.
*
* @return a new (free) local TCP port number
*/
static u16_t
tcp_new_port(void)
{
- int i;
+ u8_t i;
+ u16_t n = 0;
struct tcp_pcb *pcb;
-#ifndef TCP_LOCAL_PORT_RANGE_START
-/* From http://www.iana.org/assignments/port-numbers:
- "The Dynamic and/or Private Ports are those from 49152 through 65535" */
-#define TCP_LOCAL_PORT_RANGE_START 0xc000
-#define TCP_LOCAL_PORT_RANGE_END 0xffff
-#endif
- static u16_t port = TCP_LOCAL_PORT_RANGE_START;
- again:
- if (port++ >= TCP_LOCAL_PORT_RANGE_END) {
- port = TCP_LOCAL_PORT_RANGE_START;
+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 == port) {
+ if (pcb->local_port == tcp_port) {
+ if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
+ return 0;
+ }
goto again;
}
}
}
- return port;
+ return tcp_port;
}
/**
@@ -683,9 +714,12 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
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 ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ 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;
@@ -735,7 +769,7 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
if (old_local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
- TCP_REG(&tcp_active_pcbs, pcb);
+ TCP_REG_ACTIVE(pcb);
snmp_inc_tcpactiveopens();
tcp_output(pcb);
@@ -762,7 +796,9 @@ tcp_slowtmr(void)
err = ERR_OK;
++tcp_ticks;
+ ++tcp_timer_ctr;
+tcp_slowtmr_start:
/* Steps through all of the active PCBs. */
prev = NULL;
pcb = tcp_active_pcbs;
@@ -774,6 +810,12 @@ tcp_slowtmr(void)
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;
@@ -799,8 +841,9 @@ tcp_slowtmr(void)
}
} else {
/* Increase the retransmission timer if it is running */
- if(pcb->rtime >= 0)
+ if(pcb->rtime >= 0) {
++pcb->rtime;
+ }
if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
/* Time for a retransmission. */
@@ -836,25 +879,24 @@ tcp_slowtmr(void)
}
/* Check if this PCB has stayed too long in FIN-WAIT-2 */
if (pcb->state == FIN_WAIT_2) {
- if ((u32_t)(tcp_ticks - pcb->tmr) >
- TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
- ++pcb_remove;
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+ /* 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((pcb->so_options & SOF_KEEPALIVE) &&
+ if(ip_get_option(pcb, SOF_KEEPALIVE) &&
((pcb->state == ESTABLISHED) ||
(pcb->state == CLOSE_WAIT))) {
-#if LWIP_TCP_KEEPALIVE
- if((u32_t)(tcp_ticks - pcb->tmr) >
- (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl))
- / TCP_SLOW_INTERVAL)
-#else
if((u32_t)(tcp_ticks - pcb->tmr) >
- (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
-#endif /* LWIP_TCP_KEEPALIVE */
+ (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)
{
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
@@ -863,15 +905,9 @@ tcp_slowtmr(void)
++pcb_remove;
++pcb_reset;
}
-#if LWIP_TCP_KEEPALIVE
- else if((u32_t)(tcp_ticks - pcb->tmr) >
- (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl)
- / TCP_SLOW_INTERVAL)
-#else
else if((u32_t)(tcp_ticks - pcb->tmr) >
- (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT)
+ (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
/ TCP_SLOW_INTERVAL)
-#endif /* LWIP_TCP_KEEPALIVE */
{
tcp_keepalive(pcb);
pcb->keep_cnt_sent++;
@@ -910,6 +946,8 @@ tcp_slowtmr(void)
/* 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) {
@@ -921,15 +959,22 @@ tcp_slowtmr(void)
tcp_active_pcbs = pcb->next;
}
- TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT);
if (pcb_reset) {
tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
pcb->local_port, pcb->remote_port);
}
+ 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;
@@ -940,7 +985,11 @@ tcp_slowtmr(void)
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);
@@ -996,34 +1045,78 @@ tcp_slowtmr(void)
void
tcp_fasttmr(void)
{
- struct tcp_pcb *pcb = tcp_active_pcbs;
+ struct tcp_pcb *pcb;
+
+ ++tcp_timer_ctr;
+
+tcp_fasttmr_start:
+ pcb = tcp_active_pcbs;
while(pcb != NULL) {
- struct tcp_pcb *next = pcb->next;
- /* If there is data which was previously "refused" by upper layer */
- if (pcb->refused_data != NULL) {
- /* Notify again application with data previously received. */
- err_t err;
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n"));
- TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
- if (err == ERR_OK) {
- pcb->refused_data = NULL;
- } else if (err == ERR_ABRT) {
- /* if err == ERR_ABRT, 'pcb' is already deallocated */
- pcb = NULL;
+ 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);
}
- }
- /* send delayed ACKs */
- if (pcb && (pcb->flags & TF_ACK_DELAY)) {
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
- tcp_ack_now(pcb);
- tcp_output(pcb);
- pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ 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;
}
+ }
+}
- pcb = next;
+/** Pass pcb->refused_data to the recv callback */
+err_t
+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;
}
/**
@@ -1115,7 +1208,8 @@ tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
#endif /* LWIP_CALLBACK_API */
/**
- * Kills the oldest active connection that has lower priority than prio.
+ * Kills the oldest active connection that has the same or lower priority than
+ * 'prio'.
*
* @param prio minimum priority
*/
@@ -1232,6 +1326,7 @@ tcp_alloc(u8_t prio)
pcb->lastack = iss;
pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
+ pcb->last_timer = tcp_timer_ctr;
pcb->polltmr = 0;
@@ -1279,7 +1374,9 @@ tcp_new(void)
*/
void
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
@@ -1294,6 +1391,7 @@ tcp_arg(struct tcp_pcb *pcb, void *arg)
void
tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
{
+ LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
pcb->recv = recv;
}
@@ -1307,6 +1405,7 @@ tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
void
tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
{
+ LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
pcb->sent = sent;
}
@@ -1321,6 +1420,7 @@ tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
void
tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
{
+ LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
pcb->errf = err;
}
@@ -1335,6 +1435,8 @@ tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
void
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 */
@@ -1349,6 +1451,7 @@ tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
void
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 */
@@ -1441,7 +1544,7 @@ tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *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_DELAY) {
pcb->flags |= TF_ACK_NOW;
tcp_output(pcb);
}
diff --git a/Libraries/Lwip/lwip/src/core/tcp_in.c b/Libraries/Lwip/lwip/src/core/tcp_in.c
index f344e447..d0ae7da7 100644
--- a/Libraries/Lwip/lwip/src/core/tcp_in.c
+++ b/Libraries/Lwip/lwip/src/core/tcp_in.c
@@ -54,7 +54,7 @@
#include "lwip/src/include/ipv4/lwip/inet_chksum.h"
#include "lwip/src/include/lwip/stats.h"
#include "lwip/src/include/lwip/snmp.h"
-#include "lwip/src/sam/include/arch/perf.h"
+#include "arch/perf.h"
/* These variables are global to all functions involved in the input
processing of TCP segments. They are set by the tcp_input()
@@ -117,20 +117,14 @@ tcp_input(struct pbuf *p, struct netif *inp)
/* drop short packets */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
TCP_STATS_INC(tcp.lenerr);
- TCP_STATS_INC(tcp.drop);
- snmp_inc_tcpinerrs();
- pbuf_free(p);
- return;
+ goto dropped;
}
/* Don't even process incoming broadcasts/multicasts. */
if (ip_addr_isbroadcast(&current_iphdr_dest, inp) ||
ip_addr_ismulticast(&current_iphdr_dest)) {
TCP_STATS_INC(tcp.proterr);
- TCP_STATS_INC(tcp.drop);
- snmp_inc_tcpinerrs();
- pbuf_free(p);
- return;
+ goto dropped;
}
#if CHECKSUM_CHECK_TCP
@@ -144,10 +138,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
tcp_debug_print(tcphdr);
#endif /* TCP_DEBUG */
TCP_STATS_INC(tcp.chkerr);
- TCP_STATS_INC(tcp.drop);
- snmp_inc_tcpinerrs();
- pbuf_free(p);
- return;
+ goto dropped;
}
#endif
@@ -158,10 +149,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
/* drop short packets */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
TCP_STATS_INC(tcp.lenerr);
- TCP_STATS_INC(tcp.drop);
- snmp_inc_tcpinerrs();
- pbuf_free(p);
- return;
+ goto dropped;
}
/* Convert fields in TCP header to host byte order. */
@@ -297,22 +285,19 @@ tcp_input(struct pbuf *p, struct netif *inp)
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) {
- /* Notify again application with data previously received. */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
- TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
- if (err == ERR_OK) {
- pcb->refused_data = NULL;
- } else if ((err == ERR_ABRT) || (tcplen > 0)) {
- /* if err == ERR_ABRT, 'pcb' is already deallocated */
- /* Drop incoming packets because pcb is "full" (only if the incoming
- segment contains data). */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+ 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();
- pbuf_free(p);
- return;
+ goto aborted;
}
}
tcp_input_pcb = pcb;
@@ -331,6 +316,12 @@ tcp_input(struct pbuf *p, struct netif *inp)
} 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 {
@@ -354,9 +345,6 @@ tcp_input(struct pbuf *p, struct netif *inp)
tcp_abort(pcb);
goto aborted;
}
- if (flags & TCP_PSH) {
- recv_data->flags |= PBUF_FLAG_PUSH;
- }
/* Notify application that data has been received. */
TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
@@ -374,14 +362,19 @@ tcp_input(struct pbuf *p, struct netif *inp)
/* If a FIN segment was received, we call the callback
function with a NULL buffer to indicate EOF. */
if (recv_flags & TF_GOT_FIN) {
- /* correct rcv_wnd as the application won't call tcp_recved()
- for the FIN's seqno */
- if (pcb->rcv_wnd != TCP_WND) {
- pcb->rcv_wnd++;
- }
- TCP_EVENT_CLOSED(pcb, err);
- if (err == ERR_ABRT) {
- goto aborted;
+ 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;
+ }
}
}
@@ -424,6 +417,11 @@ aborted:
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);
}
/**
@@ -444,15 +442,19 @@ 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 + 1, seqno + tcplen,
- ip_current_dest_addr(), ip_current_src_addr(),
- tcphdr->dest, tcphdr->src);
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
} else if (flags & TCP_SYN) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
#if TCP_LISTEN_BACKLOG
@@ -482,6 +484,7 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
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;
@@ -492,7 +495,7 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
npcb->so_options = pcb->so_options & SOF_INHERITED;
/* Register the new PCB so that we can begin receiving segments
for it. */
- TCP_REG(&tcp_active_pcbs, npcb);
+ TCP_REG_ACTIVE(npcb);
/* Parse any options in the SYN. */
tcp_parseopt(npcb);
@@ -633,6 +636,7 @@ tcp_process(struct tcp_pcb *pcb)
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;
@@ -650,6 +654,7 @@ tcp_process(struct tcp_pcb *pcb)
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 */
@@ -660,8 +665,6 @@ tcp_process(struct tcp_pcb *pcb)
pcb->nrtx = 0;
}
- tcp_seg_free(rseg);
-
/* Call the user specified function to call when sucessfully
* connected. */
TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
@@ -741,7 +744,7 @@ tcp_process(struct tcp_pcb *pcb)
("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
- TCP_RMV(&tcp_active_pcbs, pcb);
+ TCP_RMV_ACTIVE(pcb);
pcb->state = TIME_WAIT;
TCP_REG(&tcp_tw_pcbs, pcb);
} else {
@@ -758,24 +761,24 @@ tcp_process(struct tcp_pcb *pcb)
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_ack_now(pcb);
tcp_pcb_purge(pcb);
- TCP_RMV(&tcp_active_pcbs, pcb);
+ 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) {
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
tcp_pcb_purge(pcb);
- TCP_RMV(&tcp_active_pcbs, pcb);
+ 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) {
+ 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;
@@ -833,7 +836,7 @@ tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
* data, and if so frees the memory of the buffered data. Next, is places the
* segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
* is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
- * i it has been removed from the buffer.
+ * 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.
@@ -853,6 +856,12 @@ tcp_receive(struct tcp_pcb *pcb)
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;
@@ -862,9 +871,21 @@ tcp_receive(struct tcp_pcb *pcb)
(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 && pcb->persist_backoff > 0) {
+ 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));
@@ -911,8 +932,9 @@ tcp_receive(struct tcp_pcb *pcb)
/* Clause 5 */
if (pcb->lastack == ackno) {
found_dupack = 1;
- if (pcb->dupacks + 1 > pcb->dupacks)
+ 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. */
@@ -1039,6 +1061,11 @@ tcp_receive(struct tcp_pcb *pcb)
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 */
@@ -1087,8 +1114,10 @@ tcp_receive(struct tcp_pcb *pcb)
}
/* If the incoming segment contains data, we must process it
- further. */
- if (tcplen > 0) {
+ 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
@@ -1226,8 +1255,7 @@ tcp_receive(struct tcp_pcb *pcb)
pcb->ooseq = pcb->ooseq->next;
tcp_seg_free(old_ooseq);
}
- }
- else {
+ } else {
next = pcb->ooseq;
/* Remove all segments on ooseq that are covered by inseg already.
* FIN is copied from ooseq to inseg if present. */
@@ -1235,7 +1263,7 @@ tcp_receive(struct tcp_pcb *pcb)
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) &&
+ 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);
@@ -1460,8 +1488,32 @@ tcp_receive(struct tcp_pcb *pcb)
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. */
diff --git a/Libraries/Lwip/lwip/src/core/tcp_out.c b/Libraries/Lwip/lwip/src/core/tcp_out.c
index b7f58e45..8a3910f3 100644
--- a/Libraries/Lwip/lwip/src/core/tcp_out.c
+++ b/Libraries/Lwip/lwip/src/core/tcp_out.c
@@ -46,12 +46,14 @@
#include "lwip/src/include/lwip/def.h"
#include "lwip/src/include/lwip/mem.h"
#include "lwip/src/include/lwip/memp.h"
-#include "lwip/src/include/lwip/sys.h"
#include "lwip/src/include/ipv4/lwip/ip_addr.h"
#include "lwip/src/include/lwip/netif.h"
#include "lwip/src/include/ipv4/lwip/inet_chksum.h"
#include "lwip/src/include/lwip/stats.h"
#include "lwip/src/include/lwip/snmp.h"
+#if LWIP_TCP_TIMESTAMPS
+#include "lwip/src/include/lwip/sys.h"
+#endif
#include <string.h>
@@ -131,6 +133,7 @@ tcp_send_fin(struct tcp_pcb *pcb)
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;
}
}
@@ -226,7 +229,7 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
LWIP_UNUSED_ARG(apiflags);
LWIP_UNUSED_ARG(first_seg);
/* always create MSS-sized pbufs */
- alloc = TCP_MSS;
+ alloc = max_length;
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
if (length < max_length) {
/* Should we allocate an oversized pbuf, or just the minimum
@@ -366,6 +369,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
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 */
@@ -424,7 +429,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
/* Usable space at the end of the last unsent segment */
unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
- space = pcb->mss - (last_unsent->len + unsent_optlen);
+ space = mss_local - (last_unsent->len + unsent_optlen);
/*
* Phase 1: Copy data directly into an oversized pbuf.
@@ -475,7 +480,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
goto memerr;
}
#if TCP_OVERSIZE_DBGCHECK
- last_unsent->oversize_left = oversize;
+ 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
@@ -517,7 +522,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
while (pos < len) {
struct pbuf *p;
u16_t left = len - pos;
- u16_t max_len = pcb->mss - optlen;
+ 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;
@@ -527,7 +532,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
if (apiflags & TCP_WRITE_FLAG_COPY) {
/* If copy is set, memory should be allocated and data copied
* into pbuf */
- if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+ 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;
}
@@ -630,6 +635,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
}
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 */
}
@@ -810,7 +817,6 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
return ERR_OK;
}
-
#if LWIP_TCP_TIMESTAMPS
/* Build a timestamp option (12 bytes long) at the specified options pointer)
@@ -897,6 +903,10 @@ tcp_output(struct tcp_pcb *pcb)
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
@@ -915,7 +925,7 @@ tcp_output(struct tcp_pcb *pcb)
*
* If data is to be sent, we will just piggyback the ACK (see below).
*/
- if ((pcb->flags & TF_ACK_NOW) &&
+ if (pcb->flags & TF_ACK_NOW &&
(seg == NULL ||
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
return tcp_send_empty_ack(pcb);
@@ -1025,13 +1035,6 @@ tcp_output(struct tcp_pcb *pcb)
}
#endif /* TCP_OVERSIZE */
- if (seg != NULL && pcb->persist_backoff == 0 &&
- ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) {
- /* prepare for persist timer */
- pcb->persist_cnt = 0;
- pcb->persist_backoff = 1;
- }
-
pcb->flags &= ~TF_NAGLEMEMERR;
return ERR_OK;
}
@@ -1063,10 +1066,15 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
/* Add any requested options. NB MSS option is only set on SYN
packets, so ignore it here */
- LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
opts = (u32_t *)(void *)(seg->tcphdr + 1);
if (seg->flags & TF_SEG_OPTS_MSS) {
- TCP_BUILD_MSS_OPTION(*opts);
+ u16_t mss;
+#if TCP_CALCULATE_EFF_SEND_MSS
+ mss = tcp_eff_send_mss(TCP_MSS, &pcb->remote_ip);
+#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
@@ -1244,6 +1252,7 @@ tcp_rexmit_rto(struct tcp_pcb *pcb)
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;
@@ -1284,6 +1293,12 @@ tcp_rexmit(struct tcp_pcb *pcb)
}
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;
@@ -1440,9 +1455,11 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
} else {
/* Data segment, copy in one byte from the head of the unacked queue */
- struct tcp_hdr *thdr = (struct tcp_hdr *)seg->p->payload;
char *d = ((char *)p->payload + TCP_HLEN);
- pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4);
+ /* 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
diff --git a/Libraries/Lwip/lwip/src/core/udp.c b/Libraries/Lwip/lwip/src/core/udp.c
index 8202415f..470c9060 100644
--- a/Libraries/Lwip/lwip/src/core/udp.c
+++ b/Libraries/Lwip/lwip/src/core/udp.c
@@ -59,16 +59,83 @@
#include "lwip/src/include/ipv4/lwip/icmp.h"
#include "lwip/src/include/lwip/stats.h"
#include "lwip/src/include/lwip/snmp.h"
-#include "lwip/src/sam/include/arch/perf.h"
+#include "arch/perf.h"
#include "lwip/src/include/lwip/dhcp.h"
#include <string.h>
+#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
+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
+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
@@ -171,22 +238,28 @@ udp_input(struct pbuf *p, struct netif *inp)
ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port));
/* compare PCB local addr+port to UDP destination addr+port */
- if ((pcb->local_port == dest) &&
- ((!broadcast && ip_addr_isany(&pcb->local_ip)) ||
+ if (pcb->local_port == dest) {
+ if (
+ (!broadcast && ip_addr_isany(&pcb->local_ip)) ||
ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest) ||
#if LWIP_IGMP
ip_addr_ismulticast(&current_iphdr_dest) ||
#endif /* LWIP_IGMP */
#if IP_SOF_BROADCAST_RECV
- (broadcast && (pcb->so_options & SOF_BROADCAST)))) {
-#else /* IP_SOF_BROADCAST_RECV */
- (broadcast))) {
-#endif /* IP_SOF_BROADCAST_RECV */
- local_match = 1;
- if ((uncon_pcb == NULL) &&
- ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
- /* the first unconnected matching PCB */
- uncon_pcb = pcb;
+ (broadcast && ip_get_option(pcb, SOF_BROADCAST) &&
+ (ip_addr_isany(&pcb->local_ip) ||
+ ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast &&
+ (ip_addr_isany(&pcb->local_ip) ||
+ ip_addr_netcmp(&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 */
@@ -278,7 +351,7 @@ udp_input(struct pbuf *p, struct netif *inp)
snmp_inc_udpindatagrams();
#if SO_REUSE && SO_REUSE_RXTOALL
if ((broadcast || ip_addr_ismulticast(&current_iphdr_dest)) &&
- ((pcb->so_options & SOF_REUSEADDR) != 0)) {
+ 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;
@@ -293,7 +366,7 @@ udp_input(struct pbuf *p, struct netif *inp)
ip_addr_ismulticast(&current_iphdr_dest) ||
#endif /* LWIP_IGMP */
#if IP_SOF_BROADCAST_RECV
- (broadcast && (mpcb->so_options & SOF_BROADCAST)))) {
+ (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))) {
#else /* IP_SOF_BROADCAST_RECV */
(broadcast))) {
#endif /* IP_SOF_BROADCAST_RECV */
@@ -494,7 +567,7 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
#if IP_SOF_BROADCAST
/* broadcast filter? */
- if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) {
+ if (!ip_get_option(pcb, SOF_BROADCAST) && 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;
@@ -616,13 +689,9 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
#endif /* CHECKSUM_GEN_UDP */
/* output to IP */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n"));
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = &(pcb->addr_hint);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif);
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = NULL;
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, NULL);
} else
#endif /* LWIP_UDPLITE */
{ /* UDP */
@@ -655,13 +724,9 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n"));
/* output to IP */
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = &(pcb->addr_hint);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);
-#if LWIP_NETIF_HWADDRHINT
- netif->addr_hint = NULL;
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HWADDRHINT(netif, NULL);
}
/* TODO: must this be increased even if error occured? */
snmp_inc_udpoutdatagrams();
@@ -722,8 +787,8 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
PCB is alread bound to, unless *all* PCBs with that port have tha
REUSEADDR flag set. */
#if SO_REUSE
- else if (((pcb->so_options & SOF_REUSEADDR) == 0) &&
- ((ipcb->so_options & SOF_REUSEADDR) == 0)) {
+ else 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 {
@@ -745,26 +810,8 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
/* no port specified? */
if (port == 0) {
-#ifndef UDP_LOCAL_PORT_RANGE_START
-/* From http://www.iana.org/assignments/port-numbers:
- "The Dynamic and/or Private Ports are those from 49152 through 65535" */
-#define UDP_LOCAL_PORT_RANGE_START 0xc000
-#define UDP_LOCAL_PORT_RANGE_END 0xffff
-#endif
- port = UDP_LOCAL_PORT_RANGE_START;
- ipcb = udp_pcbs;
- while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {
- if (ipcb->local_port == port) {
- /* port is already used by another udp_pcb */
- port++;
- /* restart scanning all udp pcbs */
- ipcb = udp_pcbs;
- } else {
- /* go on with next udp pcb */
- ipcb = ipcb->next;
- }
- }
- if (ipcb != NULL) {
+ 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;
diff --git a/Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxyfile.doxygen b/Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxyfile.doxygen
new file mode 100644
index 00000000..5af1b79c
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxyfile.doxygen
@@ -0,0 +1,1645 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "lwIP TCP/IP Stack v141"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = .
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES += asf_license_start=" "
+ALIASES += asf_license_stop=" "
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = NO
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "[WARNING]$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = doxygen_module_mainpage.h ../../api/api_lib.c ../../api/api_msg.c ../../api/err.c ../../api/netbuf.c ../../api/netdb.c ../../api/netifapi.c ../../api/sockets.c ../../api/tcpip.c ../../core/def.c ../../core/dhcp.c ../../core/dns.c ../../core/lwip_init.c ../../core/lwip_timers_141.c ../../core/mem.c ../../core/memp.c ../../core/netif.c ../../core/pbuf.c ../../core/raw.c ../../core/stats.c ../../core/sys.c ../../core/tcp.c ../../core/tcp_in.c ../../core/tcp_out.c ../../core/udp.c ../../core/ipv4/autoip.c ../../core/ipv4/icmp.c ../../core/ipv4/igmp.c ../../core/ipv4/inet.c ../../core/ipv4/inet_chksum.c ../../core/ipv4/ip.c ../../core/ipv4/ip_addr.c ../../core/ipv4/ip_frag.c ../../netif/etharp.c ../../include/lwip/api.h ../../include/lwip/api_msg.h ../../include/lwip/arch.h ../../include/lwip/debug.h ../../include/lwip/def.h ../../include/lwip/dhcp.h ../../include/lwip/dns.h ../../include/lwip/err.h ../../include/lwip/init.h ../../include/lwip/mem.h ../../include/lwip/memp.h ../../include/lwip/memp_std.h ../../include/lwip/netbuf.h ../../include/lwip/netdb.h ../../include/lwip/netif.h ../../include/lwip/netifapi.h ../../include/lwip/opt.h ../../include/lwip/pbuf.h ../../include/lwip/raw.h ../../include/lwip/sio.h ../../include/lwip/snmp.h ../../include/lwip/snmp_asn1.h ../../include/lwip/snmp_msg.h ../../include/lwip/snmp_structs.h ../../include/lwip/sockets.h ../../include/lwip/stats.h ../../include/lwip/sys.h ../../include/lwip/tcp.h ../../include/lwip/tcp_impl.h ../../include/lwip/tcpip.h ../../include/lwip/timers.h ../../include/lwip/udp.h ../../include/netif/etharp.h ../../include/netif/ppp_oe.h ../../include/ipv4/lwip/autoip.h ../../include/ipv4/lwip/icmp.h ../../include/ipv4/lwip/igmp.h ../../include/ipv4/lwip/inet.h ../../include/ipv4/lwip/inet_chksum.h ../../include/ipv4/lwip/ip.h ../../include/ipv4/lwip/ip_addr.h ../../include/ipv4/lwip/ip_frag.h ../../module_config/lwipopts.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c \
+ *.h \
+ *.s \
+ *.S
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH = ./
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = RTF
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = YES
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH = ../../include ../../include/lwip ../../include/ipv4
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = __DOXYGEN__ __AVR32_ABI_COMPILER__ __attribute__()= __GNUC__=4
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = gif
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxygen_module_mainpage.h b/Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxygen_module_mainpage.h
new file mode 100644
index 00000000..26566747
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/doxygen/thirdparty.lwip.141/doxygen_module_mainpage.h
@@ -0,0 +1,58 @@
+
+/**
+ * Copyright (c) 2012 Atmel Corporation. All rights reserved.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * 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 Atmel may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 4. This software may only be redistributed and used in connection with an
+ * Atmel microcontroller product.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
+ *
+ * \asf_license_stop
+ *
+ * \mainpage
+ *
+ * \section intro Introduction
+ * This documentation has been automatically generated, and documents the source
+ * code found in the Atmel Software Framework (ASF). <p>
+ * Use the above menu to navigate in the documentation, or use the links below: <br>
+ * <ul>
+ * <li> <a href="globals_func.html">Functions</a>
+ * <li> <a href="annotated.html">Data structures</a>
+ * <li> <a href="globals_type.html">Defines</a>
+ * </ul>
+ *
+ * \section main_licence License
+ * <ul>
+ * <li>\ref License
+ * </ul>
+ * \section contactinfo Contact Information
+ * For further information, visit <a href="http://www.atmel.com/">Atmel</a>.\n
+ *
+ */
diff --git a/Libraries/Lwip/lwip/src/include/ipv4/lwip/autoip.h b/Libraries/Lwip/lwip/src/include/ipv4/lwip/autoip.h
index a919a099..225be946 100644
--- a/Libraries/Lwip/lwip/src/include/ipv4/lwip/autoip.h
+++ b/Libraries/Lwip/lwip/src/include/ipv4/lwip/autoip.h
@@ -89,8 +89,7 @@ struct autoip
};
-/** Init srand, has to be called before entering mainloop */
-void autoip_init(void);
+#define autoip_init() /* Compatibility define, no init needed. */
/** Set a struct autoip allocated by the application to work with */
void autoip_set_struct(struct netif *netif, struct autoip *autoip);
diff --git a/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip.h b/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip.h
index 4b1d6ccf..41b44d95 100644
--- a/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip.h
+++ b/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip.h
@@ -94,16 +94,16 @@ struct ip_pcb {
/*
* Option flags per-socket. These are the same like SO_XXX.
*/
-/*#define SOF_DEBUG (u8_t)0x01U Unimplemented: turn on debugging info recording */
-#define SOF_ACCEPTCONN (u8_t)0x02U /* socket has had listen() */
-#define SOF_REUSEADDR (u8_t)0x04U /* allow local address reuse */
-#define SOF_KEEPALIVE (u8_t)0x08U /* keep connections alive */
-/*#define SOF_DONTROUTE (u8_t)0x10U Unimplemented: just use interface addresses */
-#define SOF_BROADCAST (u8_t)0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
-/*#define SOF_USELOOPBACK (u8_t)0x40U Unimplemented: bypass hardware when possible */
-#define SOF_LINGER (u8_t)0x80U /* linger on close if data present */
-/*#define SOF_OOBINLINE (u16_t)0x0100U Unimplemented: leave received OOB data in line */
-/*#define SOF_REUSEPORT (u16_t)0x0200U Unimplemented: allow local address & port reuse */
+/*#define SOF_DEBUG 0x01U Unimplemented: turn on debugging info recording */
+#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */
+#define SOF_REUSEADDR 0x04U /* allow local address reuse */
+#define SOF_KEEPALIVE 0x08U /* keep connections alive */
+/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */
+#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */
+#define SOF_LINGER 0x80U /* linger on close if data present */
+/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */
+/*#define SOF_REUSEPORT 0x0200U Unimplemented: allow local address & port reuse */
/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */
#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/)
@@ -114,8 +114,10 @@ struct ip_pcb {
#endif
PACK_STRUCT_BEGIN
struct ip_hdr {
- /* version / header length / type of service */
- PACK_STRUCT_FIELD(u16_t _v_hl_tos);
+ /* version / header length */
+ PACK_STRUCT_FIELD(u8_t _v_hl);
+ /* type of service */
+ PACK_STRUCT_FIELD(u8_t _tos);
/* total length */
PACK_STRUCT_FIELD(u16_t _len);
/* identification */
@@ -141,9 +143,9 @@ PACK_STRUCT_END
# include "arch/epstruct.h"
#endif
-#define IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12)
-#define IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f)
-#define IPH_TOS(hdr) (ntohs((hdr)->_v_hl_tos) & 0xff)
+#define IPH_V(hdr) ((hdr)->_v_hl >> 4)
+#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f)
+#define IPH_TOS(hdr) ((hdr)->_tos)
#define IPH_LEN(hdr) ((hdr)->_len)
#define IPH_ID(hdr) ((hdr)->_id)
#define IPH_OFFSET(hdr) ((hdr)->_offset)
@@ -151,7 +153,8 @@ PACK_STRUCT_END
#define IPH_PROTO(hdr) ((hdr)->_proto)
#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
-#define IPH_VHLTOS_SET(hdr, v, hl, tos) (hdr)->_v_hl_tos = (htons(((v) << 12) | ((hl) << 8) | (tos)))
+#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl))
+#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos)
#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
#define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
@@ -198,6 +201,13 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
/** Destination IP address of current_header */
#define ip_current_dest_addr() (&current_iphdr_dest)
+/** Gets an IP pcb option (SOF_* flags) */
+#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt))
+/** Sets an IP pcb option (SOF_* flags) */
+#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
+/** Resets an IP pcb option (SOF_* flags) */
+#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
+
#if IP_DEBUG
void ip_debug_print(struct pbuf *p);
#else
diff --git a/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip_addr.h b/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip_addr.h
index aad067fa..0b710d42 100644
--- a/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip_addr.h
+++ b/Libraries/Lwip/lwip/src/include/ipv4/lwip/ip_addr.h
@@ -210,7 +210,7 @@ u8_t ip4_addr_netmask_valid(u32_t netmask);
#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL))
#define ip_addr_debug_print(debug, ipaddr) \
- LWIP_DEBUGF(debug, ("%" U16_F".%" U16_F".%" U16_F".%" U16_F, \
+ LWIP_DEBUGF(debug, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, \
ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \
ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \
ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \
diff --git a/Libraries/Lwip/lwip/src/include/ipv6/lwip/icmp.h b/Libraries/Lwip/lwip/src/include/ipv6/lwip/icmp.h
new file mode 100644
index 00000000..87e9ffd9
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/include/ipv6/lwip/icmp.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ICMP_H__
+#define __LWIP_ICMP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ICMP6_DUR 1
+#define ICMP6_TE 3
+#define ICMP6_ECHO 128 /* echo */
+#define ICMP6_ER 129 /* echo reply */
+
+
+enum icmp_dur_type {
+ ICMP_DUR_NET = 0, /* net unreachable */
+ ICMP_DUR_HOST = 1, /* host unreachable */
+ ICMP_DUR_PROTO = 2, /* protocol unreachable */
+ ICMP_DUR_PORT = 3, /* port unreachable */
+ ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */
+ ICMP_DUR_SR = 5 /* source route failed */
+};
+
+enum icmp_te_type {
+ ICMP_TE_TTL = 0, /* time to live exceeded in transit */
+ ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */
+};
+
+void icmp_input(struct pbuf *p, struct netif *inp);
+
+void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
+void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
+
+struct icmp_echo_hdr {
+ u8_t type;
+ u8_t icode;
+ u16_t chksum;
+ u16_t id;
+ u16_t seqno;
+};
+
+struct icmp_dur_hdr {
+ u8_t type;
+ u8_t icode;
+ u16_t chksum;
+ u32_t unused;
+};
+
+struct icmp_te_hdr {
+ u8_t type;
+ u8_t icode;
+ u16_t chksum;
+ u32_t unused;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ICMP */
+
+#endif /* __LWIP_ICMP_H__ */
+
diff --git a/Libraries/Lwip/lwip/src/include/ipv6/lwip/inet.h b/Libraries/Lwip/lwip/src/include/ipv6/lwip/inet.h
new file mode 100644
index 00000000..de1a0b63
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/include/ipv6/lwip/inet.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_INET_H__
+#define __LWIP_INET_H__
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16_t inet_chksum(void *data, u16_t len);
+u16_t inet_chksum_pbuf(struct pbuf *p);
+u16_t inet_chksum_pseudo(struct pbuf *p,
+ struct ip_addr *src, struct ip_addr *dest,
+ u8_t proto, u32_t proto_len);
+
+u32_t inet_addr(const char *cp);
+s8_t inet_aton(const char *cp, struct in_addr *addr);
+
+#ifndef _MACHINE_ENDIAN_H_
+#ifndef _NETINET_IN_H
+#ifndef _LINUX_BYTEORDER_GENERIC_H
+u16_t htons(u16_t n);
+u16_t ntohs(u16_t n);
+u32_t htonl(u32_t n);
+u32_t ntohl(u32_t n);
+#endif /* _LINUX_BYTEORDER_GENERIC_H */
+#endif /* _NETINET_IN_H */
+#endif /* _MACHINE_ENDIAN_H_ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_INET_H__ */
+
diff --git a/Libraries/Lwip/lwip/src/include/ipv6/lwip/ip.h b/Libraries/Lwip/lwip/src/include/ipv6/lwip/ip.h
new file mode 100644
index 00000000..a01cfc65
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/include/ipv6/lwip/ip.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_IP_H__
+#define __LWIP_IP_H__
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IP_HLEN 40
+
+#define IP_PROTO_ICMP 58
+#define IP_PROTO_UDP 17
+#define IP_PROTO_UDPLITE 136
+#define IP_PROTO_TCP 6
+
+/* This is passed as the destination address to ip_output_if (not
+ to ip_output), meaning that an IP header already is constructed
+ in the pbuf. This is used when TCP retransmits. */
+#ifdef IP_HDRINCL
+#undef IP_HDRINCL
+#endif /* IP_HDRINCL */
+#define IP_HDRINCL NULL
+
+#if LWIP_NETIF_HWADDRHINT
+#define IP_PCB_ADDRHINT ;u8_t addr_hint
+#else
+#define IP_PCB_ADDRHINT
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+/* This is the common part of all PCB types. It needs to be at the
+ beginning of a PCB type definition. It is located here so that
+ changes to this common part are made in one location instead of
+ having to change all PCB structs. */
+#define IP_PCB struct ip_addr local_ip; \
+ struct ip_addr remote_ip; \
+ /* Socket options */ \
+ u16_t so_options; \
+ /* Type Of Service */ \
+ u8_t tos; \
+ /* Time To Live */ \
+ u8_t ttl; \
+ /* link layer address resolution hint */ \
+ IP_PCB_ADDRHINT
+
+
+/* The IPv6 header. */
+struct ip_hdr {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u8_t tclass1:4, v:4;
+ u8_t flow1:4, tclass2:4;
+#else
+ u8_t v:4, tclass1:4;
+ u8_t tclass2:8, flow1:4;
+#endif
+ u16_t flow2;
+ u16_t len; /* payload length */
+ u8_t nexthdr; /* next header */
+ u8_t hoplim; /* hop limit (TTL) */
+ struct ip_addr src, dest; /* source and destination IP addresses */
+};
+
+#define IPH_PROTO(hdr) (iphdr->nexthdr)
+
+void ip_init(void);
+
+#include "lwip/netif.h"
+
+struct netif *ip_route(struct ip_addr *dest);
+
+void ip_input(struct pbuf *p, struct netif *inp);
+
+/* source and destination addresses in network byte order, please */
+err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+ u8_t ttl, u8_t proto);
+
+err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+ u8_t ttl, u8_t proto,
+ struct netif *netif);
+
+#define ip_current_netif() NULL
+#define ip_current_header() NULL
+
+#if IP_DEBUG
+void ip_debug_print(struct pbuf *p);
+#endif /* IP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_H__ */
+
+
diff --git a/Libraries/Lwip/lwip/src/include/ipv6/lwip/ip_addr.h b/Libraries/Lwip/lwip/src/include/ipv6/lwip/ip_addr.h
new file mode 100644
index 00000000..b2d8ae56
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/include/ipv6/lwip/ip_addr.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_IP_ADDR_H__
+#define __LWIP_IP_ADDR_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IP_ADDR_ANY 0
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+ struct ip_addr {
+ PACK_STRUCT_FIELD(u32_t addr[4]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/*
+ * struct ipaddr2 is used in the definition of the ARP packet format in
+ * order to support compilers that don't have structure packing.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_addr2 {
+ PACK_STRUCT_FIELD(u16_t addrw[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP6_ADDR(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = htonl((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \
+ (ipaddr)->addr[1] = htonl(((c & 0xffff) << 16) | (d & 0xffff)); \
+ (ipaddr)->addr[2] = htonl(((e & 0xffff) << 16) | (f & 0xffff)); \
+ (ipaddr)->addr[3] = htonl(((g & 0xffff) << 16) | (h & 0xffff)); } while(0)
+
+u8_t ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2,
+ struct ip_addr *mask);
+u8_t ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2);
+void ip_addr_set(struct ip_addr *dest, struct ip_addr *src);
+u8_t ip_addr_isany(struct ip_addr *addr);
+
+#define ip_addr_debug_print(debug, ipaddr) \
+ LWIP_DEBUGF(debug, ("%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F"\n", \
+ (ntohl(ipaddr->addr[0]) >> 16) & 0xffff, \
+ ntohl(ipaddr->addr[0]) & 0xffff, \
+ (ntohl(ipaddr->addr[1]) >> 16) & 0xffff, \
+ ntohl(ipaddr->addr[1]) & 0xffff, \
+ (ntohl(ipaddr->addr[2]) >> 16) & 0xffff, \
+ ntohl(ipaddr->addr[2]) & 0xffff, \
+ (ntohl(ipaddr->addr[3]) >> 16) & 0xffff, \
+ ntohl(ipaddr->addr[3]) & 0xffff));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_ADDR_H__ */
diff --git a/Libraries/Lwip/lwip/src/include/lwip/api.h b/Libraries/Lwip/lwip/src/include/lwip/api.h
index 5af6d278..3f0b005c 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/api.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/api.h
@@ -38,10 +38,10 @@
#include <stddef.h> /* for size_t */
-#include "lwip/netbuf.h"
-#include "lwip/sys.h"
-#include "lwip/ip_addr.h"
-#include "lwip/err.h"
+#include "lwip/src/include/lwip/netbuf.h"
+#include "lwip/src/include/lwip/sys.h"
+#include "lwip/src/include/ipv4/lwip/ip_addr.h"
+#include "lwip/src/include/lwip/err.h"
#ifdef __cplusplus
extern "C" {
@@ -159,6 +159,11 @@ struct netconn {
#if LWIP_SOCKET
int socket;
#endif /* LWIP_SOCKET */
+#if LWIP_SO_SNDTIMEO
+ /** timeout to wait for sending data (which means enqueueing data for sending
+ in internal buffers) */
+ s32_t send_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVTIMEO
/** timeout to wait for new data to be received
(or connections to arrive for listening netconns) */
@@ -230,8 +235,10 @@ void netconn_recved(struct netconn *conn, u32_t length);
err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
ip_addr_t *addr, u16_t port);
err_t netconn_send(struct netconn *conn, struct netbuf *buf);
-err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size,
- u8_t apiflags);
+err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags, size_t *bytes_written);
+#define netconn_write(conn, dataptr, size, apiflags) \
+ netconn_write_partly(conn, dataptr, size, apiflags, NULL)
err_t netconn_close(struct netconn *conn);
err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
@@ -262,6 +269,12 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0)
+#if LWIP_SO_SNDTIMEO
+/** Set the send timeout in milliseconds */
+#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout))
+/** Get the send timeout in milliseconds */
+#define netconn_get_sendtimeout(conn) ((conn)->send_timeout)
+#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
/** Set the receive timeout in milliseconds */
#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout))
diff --git a/Libraries/Lwip/lwip/src/include/lwip/api_msg.h b/Libraries/Lwip/lwip/src/include/lwip/api_msg.h
index fc5d7b6c..43be3a7c 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/api_msg.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/api_msg.h
@@ -38,10 +38,10 @@
#include <stddef.h> /* for size_t */
-#include "lwip/src/include/lwip/ip_addr.h"
+#include "lwip/src/include/ipv4/lwip/ip_addr.h"
#include "lwip/src/include/lwip/err.h"
#include "lwip/src/include/lwip/sys.h"
-#include "lwip/src/include/lwip/igmp.h"
+#include "lwip/src/include/ipv4/lwip/igmp.h"
#include "lwip/src/include/lwip/api.h"
#ifdef __cplusplus
@@ -89,6 +89,9 @@ struct api_msg_msg {
const void *dataptr;
size_t len;
u8_t apiflags;
+#if LWIP_SO_SNDTIMEO
+ u32_t time_started;
+#endif /* LWIP_SO_SNDTIMEO */
} w;
/** used for do_recv */
struct {
diff --git a/Libraries/Lwip/lwip/src/include/lwip/arch.h b/Libraries/Lwip/lwip/src/include/lwip/arch.h
index b76d1174..4d6df773 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/arch.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/arch.h
@@ -40,7 +40,7 @@
#define BIG_ENDIAN 4321
#endif
-#include "lwip/src/sam/include/arch/cc.h"
+#include "arch/cc.h"
/** Temporary: define format string for size_t if not defined in cc.h */
#ifndef SZT_F
@@ -204,27 +204,6 @@ extern "C" {
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
-
-#define ENSROK 0 /* DNS server returned answer with no data */
-#define ENSRNODATA 160 /* DNS server returned answer with no data */
-#define ENSRFORMERR 161 /* DNS server claims query was misformatted */
-#define ENSRSERVFAIL 162 /* DNS server returned general failure */
-#define ENSRNOTFOUND 163 /* Domain name not found */
-#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */
-#define ENSRREFUSED 165 /* DNS server refused query */
-#define ENSRBADQUERY 166 /* Misformatted DNS query */
-#define ENSRBADNAME 167 /* Misformatted domain name */
-#define ENSRBADFAMILY 168 /* Unsupported address family */
-#define ENSRBADRESP 169 /* Misformatted DNS reply */
-#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */
-#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */
-#define ENSROF 172 /* End of file */
-#define ENSRFILE 173 /* Error reading file */
-#define ENSRNOMEM 174 /* Out of memory */
-#define ENSRDESTRUCTION 175 /* Application terminated lookup */
-#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */
-#define ENSRCNAMELOOP 177 /* Domain name is too long */
-
#ifndef errno
extern int errno;
#endif
diff --git a/Libraries/Lwip/lwip/src/include/lwip/debug.h b/Libraries/Lwip/lwip/src/include/lwip/debug.h
index c65eb371..8bd97ca6 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/debug.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/debug.h
@@ -33,6 +33,7 @@
#define __LWIP_DEBUG_H__
#include "lwip/src/include/lwip/arch.h"
+#include "lwip/src/include/lwip/opt.h"
/** lower two bits indicate debug level
* - 0 all
@@ -91,7 +92,7 @@
} while(0)
#else /* LWIP_DEBUG */
-#define LWIP_DEBUGF(debug, message)
+#define LWIP_DEBUGF(debug, message)
#endif /* LWIP_DEBUG */
#endif /* __LWIP_DEBUG_H__ */
diff --git a/Libraries/Lwip/lwip/src/include/lwip/def.h b/Libraries/Lwip/lwip/src/include/lwip/def.h
index 1cbd16f4..b5521c5f 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/def.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/def.h
@@ -44,14 +44,9 @@ extern "C" {
#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y))
#ifndef NULL
-#define NULL 0
-//#define NULL ((void *)0)
+#define NULL ((void *)0)
#endif
-/** Get the absolute difference between 2 u32_t values (correcting overflows)
- * 'a' is expected to be 'higher' (without overflow) than 'b'. */
-#define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1)))
-
/* Endianess-optimized shifting of two u8_t to create one u16_t */
#if BYTE_ORDER == LITTLE_ENDIAN
#define LWIP_MAKE_U16(a, b) ((a << 8) | b)
diff --git a/Libraries/Lwip/lwip/src/include/lwip/init.h b/Libraries/Lwip/lwip/src/include/lwip/init.h
index ef7a8d3b..37566f60 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/init.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/init.h
@@ -43,7 +43,7 @@ extern "C" {
/** x.X.x: Minor version of the stack */
#define LWIP_VERSION_MINOR 4U
/** x.x.X: Revision of the stack */
-#define LWIP_VERSION_REVISION 0U
+#define LWIP_VERSION_REVISION 1U
/** For release candidates, this is set to 1..254
* For official releases, this is set to 255 (LWIP_RC_RELEASE)
* For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */
diff --git a/Libraries/Lwip/lwip/src/include/lwip/mem.h b/Libraries/Lwip/lwip/src/include/lwip/mem.h
index 58cec3ca..4a5d4bb4 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/mem.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/mem.h
@@ -43,6 +43,7 @@ extern "C" {
#include <stddef.h> /* for size_t */
typedef size_t mem_size_t;
+#define MEM_SIZE_F SZT_F
/* aliases for C library malloc() */
#define mem_init()
diff --git a/Libraries/Lwip/lwip/src/include/lwip/memp_std.h b/Libraries/Lwip/lwip/src/include/lwip/memp_std.h
index 6ce408fb..461ed1ac 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/memp_std.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/memp_std.h
@@ -63,9 +63,9 @@ LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg),
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
#endif /* NO_SYS==0 */
-#if ARP_QUEUEING
+#if LWIP_ARP && ARP_QUEUEING
LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE")
-#endif /* ARP_QUEUEING */
+#endif /* LWIP_ARP && ARP_QUEUEING */
#if LWIP_IGMP
LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP")
diff --git a/Libraries/Lwip/lwip/src/include/lwip/netif.h b/Libraries/Lwip/lwip/src/include/lwip/netif.h
index 850b36f6..93bdb29a 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/netif.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/netif.h
@@ -163,6 +163,10 @@ struct netif {
*/
netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+ /** This function is called when the netif has been removed */
+ netif_status_callback_fn remove_callback;
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
@@ -280,6 +284,9 @@ void netif_set_down(struct netif *netif);
#if LWIP_NETIF_STATUS_CALLBACK
void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback);
#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback);
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
void netif_set_link_up(struct netif *netif);
void netif_set_link_down(struct netif *netif);
@@ -308,6 +315,12 @@ void netif_poll_all(void);
#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
#endif /* ENABLE_LOOPBACK */
+#if LWIP_NETIF_HWADDRHINT
+#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint))
+#else /* LWIP_NETIF_HWADDRHINT */
+#define NETIF_SET_HWADDRHINT(netif, hint)
+#endif /* LWIP_NETIF_HWADDRHINT */
+
#ifdef __cplusplus
}
#endif
diff --git a/Libraries/Lwip/lwip/src/include/lwip/opt.h b/Libraries/Lwip/lwip/src/include/lwip/opt.h
index aa43051d..e2f93fe3 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/opt.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/opt.h
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * 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,
@@ -17,21 +17,21 @@
* 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.
+ * 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
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -60,7 +60,7 @@
#define SYS_LIGHTWEIGHT_PROT 0
#endif
-/**
+/**
* NO_SYS==1: Provides VERY minimal functionality. Otherwise,
* use lwIP facilities.
*/
@@ -73,7 +73,7 @@
* Mainly for compatibility to old versions.
*/
#ifndef NO_SYS_NO_TIMERS
-#define NO_SYS_NO_TIMERS 1
+#define NO_SYS_NO_TIMERS 0
#endif
/**
@@ -183,8 +183,8 @@
/**
* MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
* that defines additional pools beyond the "standard" ones required
- * by lwIP. If you set this to 1, you must have lwippools.h in your
- * inlude path somewhere.
+ * by lwIP. If you set this to 1, you must have lwippools.h in your
+ * inlude path somewhere.
*/
#ifndef MEMP_USE_CUSTOM_POOLS
#define MEMP_USE_CUSTOM_POOLS 0
@@ -309,9 +309,11 @@
/**
* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
* (requires NO_SYS==0)
+ * The default number of timeouts is calculated here for all enabled modules.
+ * The formula expects settings to be either '0' or '1'.
*/
#ifndef MEMP_NUM_SYS_TIMEOUT
-#define MEMP_NUM_SYS_TIMEOUT 3
+#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT)
#endif
/**
@@ -332,7 +334,7 @@
/**
* MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used
- * for callback/timeout API communication.
+ * for callback/timeout API communication.
* (only needed if you use tcpip.c)
*/
#ifndef MEMP_NUM_TCPIP_MSG_API
@@ -341,7 +343,7 @@
/**
* MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used
- * for incoming packets.
+ * for incoming packets.
* (only needed if you use tcpip.c)
*/
#ifndef MEMP_NUM_TCPIP_MSG_INPKT
@@ -406,7 +408,7 @@
#endif
/**
- * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
*/
#ifndef PBUF_POOL_SIZE
#define PBUF_POOL_SIZE 16
@@ -453,7 +455,7 @@
* Also notice that this slows down input processing of every IP packet!
*/
#ifndef ETHARP_TRUST_IP_MAC
-#define ETHARP_TRUST_IP_MAC 1
+#define ETHARP_TRUST_IP_MAC 0
#endif
/**
@@ -461,6 +463,8 @@
* Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check.
* If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted.
* If ETHARP_VLAN_CHECK is not defined, all traffic is accepted.
+ * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan)
+ * that returns 1 to accept a packet or 0 to drop a packet.
*/
#ifndef ETHARP_SUPPORT_VLAN
#define ETHARP_SUPPORT_VLAN 0
@@ -593,6 +597,26 @@
#define IP_SOF_BROADCAST_RECV 0
#endif
+/**
+ * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back
+ * out on the netif where it was received. This should only be used for
+ * wireless networks.
+ * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming
+ * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags!
+ */
+#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0
+#endif
+
+/**
+ * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first
+ * local TCP/UDP pcb (default==0). This can prevent creating predictable port
+ * numbers after booting a device.
+ */
+#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS
+#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0
+#endif
+
/*
----------------------------------
---------- ICMP options ----------
@@ -617,7 +641,7 @@
* LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only)
*/
#ifndef LWIP_BROADCAST_PING
-#define LWIP_BROADCAST_PING 1
+#define LWIP_BROADCAST_PING 0
#endif
/**
@@ -665,6 +689,10 @@
#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP))
#endif
+#ifndef LWIP_DHCP_BOOTP_FILE
+#define LWIP_DHCP_BOOTP_FILE 0
+#endif
+
/*
------------------------------------
---------- AUTOIP options ----------
@@ -727,7 +755,7 @@
#endif
/**
- * SNMP_PRIVATE_MIB:
+ * 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.
*/
@@ -775,7 +803,7 @@
----------------------------------
*/
/**
- * LWIP_IGMP==1: Turn on IGMP module.
+ * LWIP_IGMP==1: Turn on IGMP module.
*/
#ifndef LWIP_IGMP
#define LWIP_IGMP 0
@@ -892,12 +920,12 @@
#endif
/**
- * TCP_WND: The size of a TCP window. This must be at least
+ * TCP_WND: The size of a TCP window. This must be at least
* (2 * TCP_MSS) for things to work well
*/
#ifndef TCP_WND
#define TCP_WND (4 * TCP_MSS)
-#endif
+#endif
/**
* TCP_MAXRTX: Maximum number of retransmissions of data segments.
@@ -946,10 +974,11 @@
/**
- * TCP_SND_BUF: TCP sender buffer space (bytes).
+ * TCP_SND_BUF: TCP sender buffer space (bytes).
+ * To achieve good performance, this should be at least 2 * TCP_MSS.
*/
#ifndef TCP_SND_BUF
-#define TCP_SND_BUF 256
+#define TCP_SND_BUF (2 * TCP_MSS)
#endif
/**
@@ -966,16 +995,32 @@
* TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
*/
#ifndef TCP_SNDLOWAT
-#define TCP_SNDLOWAT ((TCP_SND_BUF)/2)
+#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1)
#endif
/**
- * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater
+ * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less
* than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
* this number, select returns writable (combined with TCP_SNDLOWAT).
*/
#ifndef TCP_SNDQUEUELOWAT
-#define TCP_SNDQUEUELOWAT ((TCP_SND_QUEUELEN)/2)
+#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5)
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0.
+ */
+#ifndef TCP_OOSEQ_MAX_BYTES
+#define TCP_OOSEQ_MAX_BYTES 0
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0.
+ */
+#ifndef TCP_OOSEQ_MAX_PBUFS
+#define TCP_OOSEQ_MAX_PBUFS 0
#endif
/**
@@ -1032,14 +1077,11 @@
* LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
* events (accept, sent, etc) that happen in the system.
* LWIP_CALLBACK_API==1: The PCB callback function is called directly
- * for the event.
+ * for the event. This is the default.
*/
-#ifndef LWIP_EVENT_API
+#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API)
#define LWIP_EVENT_API 0
#define LWIP_CALLBACK_API 1
-#else
-#define LWIP_EVENT_API 1
-#define LWIP_CALLBACK_API 0
#endif
@@ -1103,6 +1145,14 @@
#endif
/**
+ * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called
+ * when a netif has been removed
+ */
+#ifndef LWIP_NETIF_REMOVE_CALLBACK
+#define LWIP_NETIF_REMOVE_CALLBACK 0
+#endif
+
+/**
* LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table
* indices) in struct netif. TCP and UDP can make use of this to prevent
* scanning the ARP table for every sent packet. While this is faster for big
@@ -1407,7 +1457,16 @@
#endif
/**
- * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing.
+ * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
+ * SO_SNDTIMEO processing.
+ */
+#ifndef LWIP_SO_SNDTIMEO
+#define LWIP_SO_SNDTIMEO 0
+#endif
+
+/**
+ * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
+ * SO_RCVTIMEO processing.
*/
#ifndef LWIP_SO_RCVTIMEO
#define LWIP_SO_RCVTIMEO 0
@@ -1461,7 +1520,7 @@
* LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
*/
#ifndef LWIP_STATS_DISPLAY
-#define LWIP_STATS_DISPLAY 1
+#define LWIP_STATS_DISPLAY 0
#endif
/**
@@ -1475,7 +1534,7 @@
* ETHARP_STATS==1: Enable etharp stats.
*/
#ifndef ETHARP_STATS
-#define ETHARP_STATS 1
+#define ETHARP_STATS (LWIP_ARP)
#endif
/**
@@ -1541,7 +1600,7 @@
* SYS_STATS==1: Enable system stats (sem and mbox counts, etc).
*/
#ifndef SYS_STATS
-#define SYS_STATS (NO_SYS == 0)
+#define SYS_STATS (NO_SYS == 0)
#endif
#else
@@ -1735,28 +1794,35 @@
#ifndef CHECKSUM_GEN_IP
#define CHECKSUM_GEN_IP 1
#endif
-
+
/**
* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
*/
#ifndef CHECKSUM_GEN_UDP
#define CHECKSUM_GEN_UDP 1
#endif
-
+
/**
* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
*/
#ifndef CHECKSUM_GEN_TCP
#define CHECKSUM_GEN_TCP 1
#endif
-
+
+/**
+ * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets.
+ */
+#ifndef CHECKSUM_GEN_ICMP
+#define CHECKSUM_GEN_ICMP 1
+#endif
+
/**
* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
*/
#ifndef CHECKSUM_CHECK_IP
#define CHECKSUM_CHECK_IP 1
#endif
-
+
/**
* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
*/
@@ -1781,6 +1847,34 @@
/*
---------------------------------------
+ ---------- Hook options ---------------
+ ---------------------------------------
+*/
+
+/* Hooks are undefined by default, define them to a function if you need them. */
+
+/**
+ * LWIP_HOOK_IP4_INPUT(pbuf, input_netif):
+ * - called from ip_input() (IPv4)
+ * - pbuf: received struct pbuf passed to ip_input()
+ * - input_netif: struct netif on which the packet has been received
+ * Return values:
+ * - 0: Hook has not consumed the packet, packet is processed as normal
+ * - != 0: Hook has consumed the packet.
+ * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook
+ * (i.e. free it when done).
+ */
+
+/**
+ * LWIP_HOOK_IP4_ROUTE(dest):
+ * - called from ip_route() (IPv4)
+ * - dest: destination IPv4 address
+ * Returns the destination netif or NULL if no destination netif is found. In
+ * that case, ip_route() continues as normal.
+ */
+
+/*
+ ---------------------------------------
---------- Debugging options ----------
---------------------------------------
*/
@@ -1805,49 +1899,49 @@
* ETHARP_DEBUG: Enable debugging in etharp.c.
*/
#ifndef ETHARP_DEBUG
-#define ETHARP_DEBUG LWIP_DBG_ON
+#define ETHARP_DEBUG LWIP_DBG_OFF
#endif
/**
* NETIF_DEBUG: Enable debugging in netif.c.
*/
#ifndef NETIF_DEBUG
-#define NETIF_DEBUG LWIP_DBG_ON
+#define NETIF_DEBUG LWIP_DBG_OFF
#endif
/**
* PBUF_DEBUG: Enable debugging in pbuf.c.
*/
#ifndef PBUF_DEBUG
-#define PBUF_DEBUG LWIP_DBG_ON
+#define PBUF_DEBUG LWIP_DBG_OFF
#endif
/**
* API_LIB_DEBUG: Enable debugging in api_lib.c.
*/
#ifndef API_LIB_DEBUG
-#define API_LIB_DEBUG LWIP_DBG_ON
+#define API_LIB_DEBUG LWIP_DBG_OFF
#endif
/**
* API_MSG_DEBUG: Enable debugging in api_msg.c.
*/
#ifndef API_MSG_DEBUG
-#define API_MSG_DEBUG LWIP_DBG_ON
+#define API_MSG_DEBUG LWIP_DBG_OFF
#endif
/**
* SOCKETS_DEBUG: Enable debugging in sockets.c.
*/
#ifndef SOCKETS_DEBUG
-#define SOCKETS_DEBUG LWIP_DBG_ON
+#define SOCKETS_DEBUG LWIP_DBG_OFF
#endif
/**
* ICMP_DEBUG: Enable debugging in icmp.c.
*/
#ifndef ICMP_DEBUG
-#define ICMP_DEBUG LWIP_DBG_ON
+#define ICMP_DEBUG LWIP_DBG_OFF
#endif
/**
@@ -1861,14 +1955,14 @@
* INET_DEBUG: Enable debugging in inet.c.
*/
#ifndef INET_DEBUG
-#define INET_DEBUG LWIP_DBG_ON
+#define INET_DEBUG LWIP_DBG_OFF
#endif
/**
* IP_DEBUG: Enable debugging for IP.
*/
#ifndef IP_DEBUG
-#define IP_DEBUG LWIP_DBG_ON
+#define IP_DEBUG LWIP_DBG_OFF
#endif
/**
@@ -1882,14 +1976,14 @@
* RAW_DEBUG: Enable debugging in raw.c.
*/
#ifndef RAW_DEBUG
-#define RAW_DEBUG LWIP_DBG_ON
+#define RAW_DEBUG LWIP_DBG_OFF
#endif
/**
* MEM_DEBUG: Enable debugging in mem.c.
*/
#ifndef MEM_DEBUG
-#define MEM_DEBUG LWIP_DBG_ON
+#define MEM_DEBUG LWIP_DBG_OFF
#endif
/**
@@ -1903,14 +1997,14 @@
* SYS_DEBUG: Enable debugging in sys.c.
*/
#ifndef SYS_DEBUG
-#define SYS_DEBUG LWIP_DBG_ON
+#define SYS_DEBUG LWIP_DBG_OFF
#endif
/**
* TIMERS_DEBUG: Enable debugging in timers.c.
*/
#ifndef TIMERS_DEBUG
-#define TIMERS_DEBUG LWIP_DBG_ON
+#define TIMERS_DEBUG LWIP_DBG_OFF
#endif
/**
@@ -1967,7 +2061,7 @@
* TCP_RST_DEBUG: Enable debugging for TCP with the RST message.
*/
#ifndef TCP_RST_DEBUG
-#define TCP_RST_DEBUG LWIP_DBG_OF
+#define TCP_RST_DEBUG LWIP_DBG_OFF
#endif
/**
diff --git a/Libraries/Lwip/lwip/src/include/lwip/pbuf.h b/Libraries/Lwip/lwip/src/include/lwip/pbuf.h
index d1b44689..59167410 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/pbuf.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/pbuf.h
@@ -69,6 +69,12 @@ typedef enum {
#define PBUF_FLAG_IS_CUSTOM 0x02U
/** indicates this pbuf is UDP multicast to be looped back */
#define PBUF_FLAG_MCASTLOOP 0x04U
+/** indicates this pbuf was received as link-level broadcast */
+#define PBUF_FLAG_LLBCAST 0x08U
+/** indicates this pbuf was received as link-level multicast */
+#define PBUF_FLAG_LLMCAST 0x10U
+/** indicates this pbuf includes a TCP FIN flag */
+#define PBUF_FLAG_TCP_FIN 0x20U
struct pbuf {
/** next pbuf in singly linked pbuf chain */
@@ -116,6 +122,24 @@ struct pbuf_custom {
};
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
+/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
+#ifndef PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_FREE_OOSEQ 1
+#endif /* PBUF_POOL_FREE_OOSEQ */
+#if NO_SYS && PBUF_POOL_FREE_OOSEQ
+extern volatile u8_t pbuf_free_ooseq_pending;
+void pbuf_free_ooseq();
+/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
+ at regular intervals from main level to check if ooseq pbufs need to be
+ freed! */
+#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \
+ /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \
+ ooseq queued pbufs now */ \
+ pbuf_free_ooseq(); }}while(0)
+#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */
+
/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
#define pbuf_init()
@@ -125,11 +149,11 @@ struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type,
struct pbuf_custom *p, void *payload_mem,
u16_t payload_mem_len);
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
-void pbuf_realloc(struct pbuf *p, u16_t size);
+void pbuf_realloc(struct pbuf *p, u16_t size);
u8_t pbuf_header(struct pbuf *p, s16_t header_size);
void pbuf_ref(struct pbuf *p);
u8_t pbuf_free(struct pbuf *p);
-u8_t pbuf_clen(struct pbuf *p);
+u8_t pbuf_clen(struct pbuf *p);
void pbuf_cat(struct pbuf *head, struct pbuf *tail);
void pbuf_chain(struct pbuf *head, struct pbuf *tail);
struct pbuf *pbuf_dechain(struct pbuf *p);
diff --git a/Libraries/Lwip/lwip/src/include/lwip/sockets.h b/Libraries/Lwip/lwip/src/include/lwip/sockets.h
index 897a4b41..959507b6 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/sockets.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/sockets.h
@@ -40,7 +40,7 @@
#include <stddef.h> /* for size_t */
-#include "lwip/src/include/lwip/ip_addr.h"
+#include "lwip/src/include/ipv4/lwip/ip_addr.h"
#include "lwip/src/include/lwip/inet.h"
#ifdef __cplusplus
@@ -62,8 +62,10 @@ struct sockaddr {
char sa_data[14];
};
-#ifndef socklen_t
-# define socklen_t u32_t
+/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED
+ to prevent this code from redefining it. */
+#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED)
+typedef u32_t socklen_t;
#endif
/* Socket protocol types (TCP/UDP/RAW) */
@@ -363,6 +365,7 @@ int lwip_fcntl(int s, int cmd, int val);
#define read(a,b,c) lwip_read(a,b,c)
#define write(a,b,c) lwip_write(a,b,c)
#define close(s) lwip_close(s)
+#define fcntl(a,b,c) lwip_fcntl(a,b,c)
#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
#endif /* LWIP_COMPAT_SOCKETS */
diff --git a/Libraries/Lwip/lwip/src/include/lwip/stats.h b/Libraries/Lwip/lwip/src/include/lwip/stats.h
index 3f052419..a8078e35 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/stats.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/stats.h
@@ -1,8 +1,8 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * 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,
@@ -11,21 +11,21 @@
* 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.
+ * 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
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -53,7 +53,7 @@ extern "C" {
#else
#define STAT_COUNTER u16_t
#define STAT_COUNTER_F U16_F
-#endif
+#endif
struct stats_proto {
STAT_COUNTER xmit; /* Transmitted packets. */
@@ -88,9 +88,9 @@ struct stats_igmp {
};
struct stats_mem {
-//#ifdef LWIP_DEBUG
+#ifdef LWIP_DEBUG
const char *name;
-//#endif /* LWIP_DEBUG */
+#endif /* LWIP_DEBUG */
mem_size_t avail;
mem_size_t used;
mem_size_t max;
@@ -271,9 +271,9 @@ void stats_init(void);
/* Display of statistics */
#if LWIP_STATS_DISPLAY
void stats_display(void);
-void stats_display_proto(struct stats_proto *proto, char *name);
+void stats_display_proto(struct stats_proto *proto, const char *name);
void stats_display_igmp(struct stats_igmp *igmp);
-void stats_display_mem(struct stats_mem *mem, char *name);
+void stats_display_mem(struct stats_mem *mem, const char *name);
void stats_display_memp(struct stats_mem *mem, int index);
void stats_display_sys(struct stats_sys *sys);
#else /* LWIP_STATS_DISPLAY */
diff --git a/Libraries/Lwip/lwip/src/include/lwip/sys.h b/Libraries/Lwip/lwip/src/include/lwip/sys.h
index 2286fd05..45935d21 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/sys.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/sys.h
@@ -1,8 +1,8 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * 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,
@@ -11,21 +11,21 @@
* 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.
+ * 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
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -46,21 +46,27 @@ typedef u8_t sys_sem_t;
typedef u8_t sys_mutex_t;
typedef u8_t sys_mbox_t;
-#define sys_sem_new(s, c) ERR_OK
+//#define sys_sem_new(s, c) ERR_OK
#define sys_sem_signal(s)
#define sys_sem_wait(s)
#define sys_arch_sem_wait(s,t)
#define sys_sem_free(s)
+#define sys_sem_valid(s) 0
+#define sys_sem_set_invalid(s)
#define sys_mutex_new(mu) ERR_OK
#define sys_mutex_lock(mu)
#define sys_mutex_unlock(mu)
#define sys_mutex_free(mu)
+#define sys_mutex_valid(mu) 0
+#define sys_mutex_set_invalid(mu)
#define sys_mbox_new(m, s) ERR_OK
#define sys_mbox_fetch(m,d)
#define sys_mbox_tryfetch(m,d)
#define sys_mbox_post(m,d)
#define sys_mbox_trypost(m,d)
#define sys_mbox_free(m)
+#define sys_mbox_valid(m)
+#define sys_mbox_set_invalid(m)
#define sys_thread_new(n,t,a,s,p)
@@ -74,9 +80,9 @@ typedef u8_t sys_mbox_t;
/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate.
* For now we use the same magic value, but we allow this to change in future.
*/
-#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
+#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
-#include "err.h"
+#include "lwip/err.h"
#include "arch/sys_arch.h"
/** Function prototype for thread functions */
@@ -113,7 +119,7 @@ void sys_mutex_lock(sys_mutex_t *mutex);
void sys_mutex_unlock(sys_mutex_t *mutex);
/** Delete a semaphore
* @param mutex the mutex to delete */
-void sys_mutex_free(sys_mutex_t *mutex);
+void sys_mutex_free(sys_mutex_t *mutex);
#ifndef sys_mutex_valid
/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */
int sys_mutex_valid(sys_mutex_t *mutex);
@@ -131,6 +137,7 @@ void sys_mutex_set_invalid(sys_mutex_t *mutex);
* @param count initial count of the semaphore
* @return ERR_OK if successful, another err_t otherwise */
err_t sys_sem_new(sys_sem_t *sem, u8_t count);
+
/** Signals a semaphore
* @param sem the semaphore to signal */
void sys_sem_signal(sys_sem_t *sem);
diff --git a/Libraries/Lwip/lwip/src/include/lwip/tcp.h b/Libraries/Lwip/lwip/src/include/lwip/tcp.h
index 4ec668bb..52752ada 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/tcp.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/tcp.h
@@ -36,7 +36,6 @@
#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-#include "lwip/src/include/lwip/sys.h"
#include "lwip/src/include/lwip/mem.h"
#include "lwip/src/include/lwip/pbuf.h"
#include "lwip/src/include/ipv4/lwip/ip.h"
@@ -156,11 +155,11 @@ enum tcp_state {
*/
#define TCP_PCB_COMMON(type) \
type *next; /* for the linked list */ \
- enum tcp_state state; /* TCP state */ \
- u8_t prio; \
void *callback_arg; \
/* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \
DEF_ACCEPT_CALLBACK \
+ enum tcp_state state; /* TCP state */ \
+ u8_t prio; \
/* ports are in host byte order */ \
u16_t local_port
@@ -187,21 +186,23 @@ struct tcp_pcb {
/* the rest of the fields are in host byte order
as we have to do some math with them */
+
+ /* Timers */
+ u8_t polltmr, pollinterval;
+ u8_t last_timer;
+ u32_t tmr;
+
/* receiver variables */
u32_t rcv_nxt; /* next seqno expected */
u16_t rcv_wnd; /* receiver window available */
u16_t rcv_ann_wnd; /* receiver window to announce */
u32_t rcv_ann_right_edge; /* announced right edge of window */
- /* Timers */
- u32_t tmr;
- u8_t polltmr, pollinterval;
-
/* Retransmission timer. */
s16_t rtime;
-
+
u16_t mss; /* maximum segment size */
-
+
/* RTT (round trip time) estimation variables */
u32_t rttest; /* RTT estimate in 500ms ticks */
u32_t rtseq; /* sequence number being timed */
@@ -211,22 +212,23 @@ struct tcp_pcb {
u8_t nrtx; /* number of retransmissions */
/* fast retransmit/recovery */
- u32_t lastack; /* Highest acknowledged seqno. */
u8_t dupacks;
-
+ u32_t lastack; /* Highest acknowledged seqno. */
+
/* congestion avoidance/control variables */
- u16_t cwnd;
+ u16_t cwnd;
u16_t ssthresh;
/* sender variables */
u32_t snd_nxt; /* next new seqno to be sent */
- u16_t snd_wnd; /* sender window */
u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
window update. */
u32_t snd_lbb; /* Sequence number of next byte to be buffered. */
+ u16_t snd_wnd; /* sender window */
+ u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */
u16_t acked;
-
+
u16_t snd_buf; /* Available buffer space for sending (in bytes). */
#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3)
u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
@@ -271,7 +273,7 @@ struct tcp_pcb {
#endif /* LWIP_TCP_KEEPALIVE */
/* Persist timer counter */
- u32_t persist_cnt;
+ u8_t persist_cnt;
/* Persist timer back-off */
u8_t persist_backoff;
@@ -333,7 +335,7 @@ void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err);
(((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0)
#else /* TCP_LISTEN_BACKLOG */
#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \
- pcb->state == LISTEN)
+ (pcb)->state == LISTEN)
#endif /* TCP_LISTEN_BACKLOG */
void tcp_recved (struct tcp_pcb *pcb, u16_t len);
diff --git a/Libraries/Lwip/lwip/src/include/lwip/tcp_impl.h b/Libraries/Lwip/lwip/src/include/lwip/tcp_impl.h
index b06c5022..e1bb93da 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/tcp_impl.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/tcp_impl.h
@@ -37,7 +37,6 @@
#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
#include "lwip/src/include/lwip/tcp.h"
-#include "lwip/src/include/lwip/sys.h"
#include "lwip/src/include/lwip/mem.h"
#include "lwip/src/include/lwip/pbuf.h"
#include "lwip/src/include/ipv4/lwip/ip.h"
@@ -51,7 +50,7 @@ extern "C" {
/* Functions for interfacing with TCP: */
/* Lower layer interface to TCP: */
-#define tcp_init() /* Compatibility define, no init needed. */
+void tcp_init (void); /* Initialize this module. */
void tcp_tmr (void); /* Must be called every
TCP_TMR_INTERVAL
ms. (Typically 250 ms). */
@@ -71,6 +70,7 @@ void tcp_rexmit (struct tcp_pcb *pcb);
void tcp_rexmit_rto (struct tcp_pcb *pcb);
void tcp_rexmit_fast (struct tcp_pcb *pcb);
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+err_t tcp_process_refused_data(struct tcp_pcb *pcb);
/**
* This is the Nagle algorithm: try to combine user data to send as few TCP
@@ -84,15 +84,16 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
(((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
- ((tpcb)->unsent->len >= (tpcb)->mss))) \
+ ((tpcb)->unsent->len >= (tpcb)->mss))) || \
+ ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \
) ? 1 : 0)
#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK)
-#define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0)
-#define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0)
-#define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0)
-#define TCP_SEQ_GEQ(a,b) ((s32_t)((a)-(b)) >= 0)
+#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0)
+#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0)
+#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0)
+#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0)
/* is b<=a<=c? */
#if 0 /* see bug #10548 */
#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b))
@@ -170,11 +171,9 @@ PACK_STRUCT_END
# include "arch/epstruct.h"
#endif
-#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8)
#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)
#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)
-#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr))
#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr))
#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags))
#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags))
@@ -301,14 +300,12 @@ struct tcp_seg {
(flags & TF_SEG_OPTS_TS ? 12 : 0)
/** This returns a TCP header option for MSS in an u32_t */
-#define TCP_BUILD_MSS_OPTION(x) (x) = PP_HTONL(((u32_t)2 << 24) | \
- ((u32_t)4 << 16) | \
- (((u32_t)TCP_MSS / 256) << 8) | \
- (TCP_MSS & 255))
+#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF))
/* Global variables: */
extern struct tcp_pcb *tcp_input_pcb;
extern u32_t tcp_ticks;
+extern u8_t tcp_active_pcbs_changed;
/* The TCP PCB lists. */
union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */
@@ -395,6 +392,24 @@ extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
#endif /* LWIP_DEBUG */
+#define TCP_REG_ACTIVE(npcb) \
+ do { \
+ TCP_REG(&tcp_active_pcbs, npcb); \
+ tcp_active_pcbs_changed = 1; \
+ } while (0)
+
+#define TCP_RMV_ACTIVE(npcb) \
+ do { \
+ TCP_RMV(&tcp_active_pcbs, npcb); \
+ tcp_active_pcbs_changed = 1; \
+ } while (0)
+
+#define TCP_PCB_REMOVE_ACTIVE(pcb) \
+ do { \
+ tcp_pcb_remove(&tcp_active_pcbs, pcb); \
+ tcp_active_pcbs_changed = 1; \
+ } while (0)
+
/* Internal functions: */
struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
diff --git a/Libraries/Lwip/lwip/src/include/lwip/tcpip.h b/Libraries/Lwip/lwip/src/include/lwip/tcpip.h
index 510fe65a..6690906c 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/tcpip.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/tcpip.h
@@ -77,6 +77,9 @@ typedef void (*tcpip_init_done_fn)(void *arg);
/** Function prototype for functions passed to tcpip_callback() */
typedef void (*tcpip_callback_fn)(void *ctx);
+/* Forward declarations */
+struct tcpip_callback_msg;
+
void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
#if LWIP_NETCONN
@@ -98,6 +101,10 @@ err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg);
err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block);
#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1)
+struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx);
+void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg);
+err_t tcpip_trycallback(struct tcpip_callback_msg* msg);
+
/* free pbufs or heap memory from another context without blocking */
err_t pbuf_free_callback(struct pbuf *p);
err_t mem_free_callback(void *m);
@@ -119,7 +126,8 @@ enum tcpip_msg_type {
TCPIP_MSG_TIMEOUT,
TCPIP_MSG_UNTIMEOUT,
#endif /* LWIP_TCPIP_TIMEOUT */
- TCPIP_MSG_CALLBACK
+ TCPIP_MSG_CALLBACK,
+ TCPIP_MSG_CALLBACK_STATIC
};
struct tcpip_msg {
diff --git a/Libraries/Lwip/lwip/src/include/lwip/timers.h b/Libraries/Lwip/lwip/src/include/lwip/timers.h
index d665624d..824f22e6 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/timers.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/timers.h
@@ -41,14 +41,16 @@
#if LWIP_TIMERS
#include "lwip/src/include/lwip/err.h"
+#if !NO_SYS
#include "lwip/src/include/lwip/sys.h"
+#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LWIP_DEBUG_TIMERNAMES
-#ifdef SYS_DEBUG
+#ifdef LWIP_DEBUG
#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG
#else /* LWIP_DEBUG */
#define LWIP_DEBUG_TIMERNAMES 0
diff --git a/Libraries/Lwip/lwip/src/include/lwip/udp.h b/Libraries/Lwip/lwip/src/include/lwip/udp.h
index 8e5bad97..57370253 100644
--- a/Libraries/Lwip/lwip/src/include/lwip/udp.h
+++ b/Libraries/Lwip/lwip/src/include/lwip/udp.h
@@ -154,7 +154,7 @@ err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
/* The following functions are the lower layer interface to UDP. */
void udp_input (struct pbuf *p, struct netif *inp);
-#define udp_init() /* Compatibility define, not init needed. */
+void udp_init (void);
#if UDP_DEBUG
void udp_debug_print(struct udp_hdr *udphdr);
diff --git a/Libraries/Lwip/lwip/src/include/netif/etharp.h b/Libraries/Lwip/lwip/src/include/netif/etharp.h
index ebaea31b..85acb143 100644
--- a/Libraries/Lwip/lwip/src/include/netif/etharp.h
+++ b/Libraries/Lwip/lwip/src/include/netif/etharp.h
@@ -190,6 +190,7 @@ err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr);
* nodes to update an entry in their ARP cache.
* From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */
#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
+void etharp_cleanup_netif(struct netif *netif);
#if ETHARP_SUPPORT_STATIC_ENTRIES
err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr);
diff --git a/Libraries/Lwip/lwip/src/include/netif/slipif.h b/Libraries/Lwip/lwip/src/include/netif/slipif.h
new file mode 100644
index 00000000..1687eead
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/include/netif/slipif.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2001, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __NETIF_SLIPIF_H__
+#define __NETIF_SLIPIF_H__
+
+#include "lwip/src/include/lwip/opt.h"
+#include "lwip/src/include/lwip/netif.h"
+
+/** Set this to 1 to start a thread that blocks reading on the serial line
+ * (using sio_read()).
+ */
+#ifndef SLIP_USE_RX_THREAD
+#define SLIP_USE_RX_THREAD !NO_SYS
+#endif
+
+/** Set this to 1 to enable functions to pass in RX bytes from ISR context.
+ * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled
+ * packets on a queue, which is fed into lwIP from slipif_poll().
+ * If disabled, slipif_poll() polls the serila line (using sio_tryread()).
+ */
+#ifndef SLIP_RX_FROM_ISR
+#define SLIP_RX_FROM_ISR 0
+#endif
+
+/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets
+ * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available.
+ * If disabled, packets will be dropped if more than one packet is received.
+ */
+#ifndef SLIP_RX_QUEUE
+#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+err_t slipif_init(struct netif * netif);
+void slipif_poll(struct netif *netif);
+#if SLIP_RX_FROM_ISR
+void slipif_process_rxqueue(struct netif *netif);
+void slipif_received_byte(struct netif *netif, u8_t data);
+void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len);
+#endif /* SLIP_RX_FROM_ISR */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/Libraries/Lwip/lwip/src/netif/etharp.c b/Libraries/Lwip/lwip/src/netif/etharp.c
index 6c5a9d67..4742a2fb 100644
--- a/Libraries/Lwip/lwip/src/netif/etharp.c
+++ b/Libraries/Lwip/lwip/src/netif/etharp.c
@@ -42,7 +42,7 @@
* This file is part of the lwIP TCP/IP stack.
*
*/
-
+
#include "lwip/src/include/lwip/opt.h"
#if LWIP_ARP || LWIP_ETHERNET
@@ -57,7 +57,7 @@
#include "lwip/src/include/netif/etharp.h"
#if PPPOE_SUPPORT
-#include "netif/ppp_oe.h"
+#include "lwip/src/include/netif/ppp_oe.h"
#endif /* PPPOE_SUPPORT */
#include <string.h>
@@ -65,13 +65,22 @@
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
+#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.
@@ -86,7 +95,11 @@ const struct eth_addr ethzero = {{0,0,0,0,0,0}};
enum etharp_state {
ETHARP_STATE_EMPTY = 0,
ETHARP_STATE_PENDING,
- ETHARP_STATE_STABLE
+ ETHARP_STATE_STABLE,
+ ETHARP_STATE_STABLE_REREQUESTING
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ ,ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
};
struct etharp_entry {
@@ -98,15 +111,10 @@ struct etharp_entry {
struct pbuf *q;
#endif /* ARP_QUEUEING */
ip_addr_t ipaddr;
- struct eth_addr ethaddr;
-#if LWIP_SNMP
struct netif *netif;
-#endif /* LWIP_SNMP */
+ struct eth_addr ethaddr;
u8_t state;
u8_t ctime;
-#if ETHARP_SUPPORT_STATIC_ENTRIES
- u8_t static_entry;
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
};
static struct etharp_entry arp_table[ARP_TABLE_SIZE];
@@ -119,7 +127,9 @@ static u8_t etharp_cached_entry;
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)) \
@@ -128,8 +138,6 @@ static u8_t etharp_cached_entry;
#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
#endif /* LWIP_NETIF_HWADDRHINT */
-static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags);
-
/* Some checks, instead of etharp_init(): */
#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
@@ -141,7 +149,7 @@ static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth
/**
* Free a complete queue of etharp entries
*
- * @param q a queue of etharp_q_entry's to free
+ * @param q a qeueue of etharp_q_entry's to free
*/
static void
free_etharp_q(struct etharp_q_entry *q)
@@ -166,28 +174,23 @@ free_etharp_q(struct etharp_q_entry *q)
/** Clean up ARP table entries */
static void
-free_entry(int i)
+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_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ 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 */
+ /* recycle entry for re-use */
arp_table[i].state = ETHARP_STATE_EMPTY;
-#if ETHARP_SUPPORT_STATIC_ENTRIES
- arp_table[i].static_entry = 0;
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
#ifdef LWIP_DEBUG
/* for debugging, clean out the complete entry */
arp_table[i].ctime = 0;
-#if LWIP_SNMP
arp_table[i].netif = NULL;
-#endif /* LWIP_SNMP */
ip_addr_set_zero(&arp_table[i].ipaddr);
arp_table[i].ethaddr = ethzero;
#endif /* LWIP_DEBUG */
@@ -210,7 +213,7 @@ etharp_tmr(void)
u8_t state = arp_table[i].state;
if (state != ETHARP_STATE_EMPTY
#if ETHARP_SUPPORT_STATIC_ENTRIES
- && (arp_table[i].static_entry == 0)
+ && (state != ETHARP_STATE_STATIC)
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
) {
arp_table[i].ctime++;
@@ -219,9 +222,14 @@ etharp_tmr(void)
(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));
+ arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
/* clean up entries that have just been expired */
- free_entry(i);
+ 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) */
@@ -255,7 +263,7 @@ etharp_tmr(void)
* entry is found or could be recycled.
*/
static s8_t
-find_entry(ip_addr_t *ipaddr, u8_t flags)
+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;
@@ -284,15 +292,15 @@ find_entry(ip_addr_t *ipaddr, u8_t flags)
u8_t state = arp_table[i].state;
/* no empty entry found yet and now we do find one? */
if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
- LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ 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);
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
/* if given, does IP address match IP address in ARP entry? */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ 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;
}
@@ -313,10 +321,10 @@ find_entry(ip_addr_t *ipaddr, u8_t flags)
}
}
/* stable entry? */
- } else if (state == ETHARP_STATE_STABLE) {
+ } else if (state >= ETHARP_STATE_STABLE) {
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* don't record old_stable for static entries since they never expire */
- if (arp_table[i].static_entry == 0)
+ if (state < ETHARP_STATE_STATIC)
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
{
/* remember entry with oldest stable entry in oldest, its age in maxtime */
@@ -334,7 +342,7 @@ find_entry(ip_addr_t *ipaddr, u8_t flags)
if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
/* or no empty entry found and not allowed to recycle? */
((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
+ 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;
}
@@ -350,34 +358,34 @@ find_entry(ip_addr_t *ipaddr, u8_t flags)
/* 1) empty entry available? */
if (empty < ARP_TABLE_SIZE) {
i = empty;
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ 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, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ 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, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ 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 free_entry) */
+ /* recycle oldest pending (queued packets are free in etharp_free_entry) */
i = old_queue;
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ 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, ("find_entry: no empty or recyclable entries found\n"));
+ 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);
- free_entry(i);
+ etharp_free_entry(i);
}
LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
@@ -390,9 +398,6 @@ find_entry(ip_addr_t *ipaddr, u8_t flags)
ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
}
arp_table[i].ctime = 0;
-#if ETHARP_SUPPORT_STATIC_ENTRIES
- arp_table[i].static_entry = 0;
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
return (err_t)i;
}
@@ -440,24 +445,23 @@ etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct
* @see pbuf_free()
*/
static err_t
-update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+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, ("update_arp_entry: %d.%d.%d.%d - %02d:%02d:%02d:%02d:%02d:%02d \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]));
-
+ 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, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ 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 = find_entry(ipaddr, flags);
+ i = etharp_find_entry(ipaddr, flags);
/* bail out if no entry could be found */
if (i < 0) {
return (err_t)i;
@@ -466,21 +470,20 @@ update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethadd
#if ETHARP_SUPPORT_STATIC_ENTRIES
if (flags & ETHARP_FLAG_STATIC_ENTRY) {
/* record static type */
- arp_table[i].static_entry = 1;
- }
+ arp_table[i].state = ETHARP_STATE_STATIC;
+ } else
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
- /* mark it stable */
- arp_table[i].state = ETHARP_STATE_STABLE;
-
-#if LWIP_SNMP
/* record network interface */
arp_table[i].netif = netif;
-#endif /* LWIP_SNMP */
/* insert in SNMP ARP index tree */
snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ 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 */
@@ -533,7 +536,7 @@ etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
return ERR_RTE;
}
- return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+ 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
@@ -552,24 +555,40 @@ etharp_remove_static_entry(ip_addr_t *ipaddr)
ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
/* find or create ARP entry */
- i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ 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_STABLE) ||
- (arp_table[i].static_entry == 0)) {
+ if (arp_table[i].state != ETHARP_STATE_STATIC) {
/* entry wasn't a static entry, cannot remove it */
return ERR_ARG;
}
/* entry found, free it */
- free_entry(i);
+ 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 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!
@@ -590,8 +609,9 @@ etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
eth_ret != NULL && ip_ret != NULL);
LWIP_UNUSED_ARG(netif);
- i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
- if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) {
+
+ 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;
@@ -645,7 +665,7 @@ etharp_ip_input(struct netif *netif, struct pbuf *p)
/* update the source IP address in the cache, if present */
/* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
* back soon (for example, if the destination IP address is ours. */
- update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_TRY_HARD);
+ etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
}
#endif /* ETHARP_TRUST_IP_MAC */
@@ -738,7 +758,7 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
can result in directly sending the queued packets for this host.
ARP message not directed to us?
-> update the source IP address in the cache, if present */
- update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
/* now act on the message itself */
@@ -815,6 +835,28 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
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
+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.
*
@@ -836,7 +878,13 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
err_t
etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
{
- struct eth_addr *dest, mcastaddr;
+ 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) {
@@ -847,8 +895,6 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
return ERR_BUF;
}
- /* assume unresolved Ethernet address */
- dest = NULL;
/* Determine on destination hardware address. Broadcasts and multicasts
* are special, other IP addresses are looked up in the ARP table. */
@@ -859,9 +905,9 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
/* multicast destination IP address? */
} else if (ip_addr_ismulticast(ipaddr)) {
/* Hash IP multicast address to MAC address.*/
- mcastaddr.addr[0] = 0x01;
- mcastaddr.addr[1] = 0x00;
- mcastaddr.addr[2] = 0x5e;
+ mcastaddr.addr[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);
@@ -869,7 +915,9 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
dest = &mcastaddr;
/* unicast destination IP address? */
} else {
- /* outside local network? */
+ 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
@@ -885,7 +933,7 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
/* interface has default gateway? */
if (!ip_addr_isany(&netif->gw)) {
/* send to hardware address of default gateway IP address */
- ipaddr = &(netif->gw);
+ dst_addr = &(netif->gw);
/* no default gateway available */
} else {
/* no route to destination error (default gateway missing) */
@@ -899,19 +947,30 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
u8_t etharp_cached_entry = *(netif->addr_hint);
if (etharp_cached_entry < ARP_TABLE_SIZE) {
#endif /* LWIP_NETIF_HWADDRHINT */
- if ((arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) &&
- (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr))) {
+ 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_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
- &arp_table[etharp_cached_entry].ethaddr);
+ return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
}
#if LWIP_NETIF_HWADDRHINT
}
}
#endif /* LWIP_NETIF_HWADDRHINT */
- /* queue on destination Ethernet address belonging to ipaddr */
- return etharp_query(netif, ipaddr, q);
+
+ /* 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 */
@@ -969,7 +1028,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
}
/* find entry in ARP cache, ask to create entry if queueing packet */
- i = find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
/* could not find or create entry? */
if (i < 0) {
@@ -989,7 +1048,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
/* { 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)));
+ (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)) {
@@ -1009,7 +1068,7 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
/* packet given? */
LWIP_ASSERT("q != NULL", q != NULL);
/* stable entry? */
- if (arp_table[i].state == ETHARP_STATE_STABLE) {
+ if (arp_table[i].state >= ETHARP_STATE_STABLE) {
/* we have a valid IP->Ethernet address mapping */
ETHARP_SET_HINT(netif, i);
/* send the packet */
@@ -1127,6 +1186,8 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
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? */
@@ -1143,6 +1204,7 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
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
@@ -1208,7 +1270,7 @@ etharp_request(struct netif *netif, ip_addr_t *ipaddr)
* calling ip_input and passing ARP frames through etharp in ethernetif_input,
* the ARP cache is protected from concurrent access.
*
- * @param p the received packet, p->payload pointing to the ethernet header
+ * @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
@@ -1216,7 +1278,9 @@ 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 */
@@ -1227,12 +1291,14 @@ ethernet_input(struct pbuf *p, struct netif *netif)
/* 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",
+ 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)) {
@@ -1243,13 +1309,17 @@ ethernet_input(struct pbuf *p, struct netif *netif)
ETHARP_STATS_INC(etharp.drop);
goto free_and_return;
}
-#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */
+#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 /* ETHARP_VLAN_CHECK */
+#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
type = vlan->tpid;
ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
}
@@ -1259,6 +1329,20 @@ ethernet_input(struct pbuf *p, struct netif *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(&ethhdr->dest, &ethbroadcast)) {
+ /* mark the pbuf as link-layer broadcast */
+ p->flags |= PBUF_FLAG_LLBCAST;
+ }
+ }
+
switch (type) {
#if LWIP_ARP
/* IP packet? */
@@ -1271,10 +1355,11 @@ ethernet_input(struct pbuf *p, struct netif *netif)
etharp_ip_input(netif, p);
#endif /* ETHARP_TRUST_IP_MAC */
/* skip Ethernet header */
- if(pbuf_header(p, -ip_hdr_offset)) {
+ 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;
@@ -1308,7 +1393,6 @@ ethernet_input(struct pbuf *p, struct netif *netif)
return ERR_OK;
free_and_return:
- //printf("Hit Free and Return \n");
pbuf_free(p);
return ERR_OK;
}
diff --git a/Libraries/Lwip/lwip/src/netif/ethernetif.c b/Libraries/Lwip/lwip/src/netif/ethernetif.c
new file mode 100644
index 00000000..8ec40be1
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ethernetif.c
@@ -0,0 +1,317 @@
+/**
+ * @file
+ * Ethernet Interface Skeleton
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * This file is a skeleton for developing Ethernet network interface
+ * drivers for lwIP. Add code to the low_level functions and do a
+ * search-and-replace for the word "ethernetif" to replace it with
+ * something that better describes your network interface.
+ */
+
+#include "lwip/opt.h"
+
+#if 0 /* don't build, this is only a skeleton, see previous comment */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include <lwip/stats.h>
+#include <lwip/snmp.h>
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'e'
+#define IFNAME1 'n'
+
+/**
+ * Helper struct to hold private data used to operate your ethernet interface.
+ * Keeping the ethernet address of the MAC in this struct is not necessary
+ * as it is already kept in the struct netif.
+ * But this is only an example, anyway...
+ */
+struct ethernetif {
+ struct eth_addr *ethaddr;
+ /* Add whatever per-interface state that is needed here. */
+};
+
+/* Forward declarations. */
+static void ethernetif_input(struct netif *netif);
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ * for this ethernetif
+ */
+static void
+low_level_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+
+ /* set MAC hardware address length */
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+ /* set MAC hardware address */
+ netif->hwaddr[0] = ;
+ ...
+ netif->hwaddr[5] = ;
+
+ /* maximum transfer unit */
+ netif->mtu = 1500;
+
+ /* device capabilities */
+ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+
+ /* Do whatever else is needed to initialize interface. */
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ * an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ * strange results. You might consider waiting for space in the DMA queue
+ * to become availale since the stack doesn't retry to send a packet
+ * dropped because of memory failure (except for the TCP timers).
+ */
+
+static err_t
+low_level_output(struct netif *netif, struct pbuf *p)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *q;
+
+ initiate transfer();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ for(q = p; q != NULL; q = q->next) {
+ /* Send the data from the pbuf to the interface, one pbuf at a
+ time. The size of the data in each pbuf is kept in the ->len
+ variable. */
+ send data from(q->payload, q->len);
+ }
+
+ signal that packet should be sent();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.xmit);
+
+ return ERR_OK;
+}
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return a pbuf filled with the received packet (including MAC header)
+ * NULL on memory error
+ */
+static struct pbuf *
+low_level_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *p, *q;
+ u16_t len;
+
+ /* Obtain the size of the packet and put it into the "len"
+ variable. */
+ len = ;
+
+#if ETH_PAD_SIZE
+ len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+
+ if (p != NULL) {
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ /* We iterate over the pbuf chain until we have read the entire
+ * packet into the pbuf. */
+ for(q = p; q != NULL; q = q->next) {
+ /* Read enough bytes to fill this pbuf in the chain. The
+ * available data in the pbuf is given by the q->len
+ * variable.
+ * This does not necessarily have to be a memcpy, you can also preallocate
+ * pbufs for a DMA-enabled MAC and after receiving truncate it to the
+ * actually received size. In this case, ensure the tot_len member of the
+ * pbuf is the sum of the chained pbuf len members.
+ */
+ read data into(q->payload, q->len);
+ }
+ acknowledge that packet has been read();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.recv);
+ } else {
+ drop packet();
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ }
+
+ return p;
+}
+
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
+static void
+ethernetif_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+ struct eth_hdr *ethhdr;
+ struct pbuf *p;
+
+ ethernetif = netif->state;
+
+ /* move received packet into a new pbuf */
+ p = low_level_input(netif);
+ /* no packet could be read, silently ignore this */
+ if (p == NULL) return;
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = p->payload;
+
+ switch (htons(ethhdr->type)) {
+ /* IP or ARP packet? */
+ case ETHTYPE_IP:
+ case ETHTYPE_ARP:
+#if PPPOE_SUPPORT
+ /* PPPoE packet? */
+ case ETHTYPE_PPPOEDISC:
+ case ETHTYPE_PPPOE:
+#endif /* PPPOE_SUPPORT */
+ /* full packet send to tcpip_thread to process */
+ if (netif->input(p, netif)!=ERR_OK)
+ { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+ pbuf_free(p);
+ p = NULL;
+ }
+ break;
+
+ default:
+ pbuf_free(p);
+ p = NULL;
+ break;
+ }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ * any other err_t on error
+ */
+err_t
+ethernetif_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+ ethernetif = mem_malloc(sizeof(struct ethernetif));
+ if (ethernetif == NULL) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
+ return ERR_MEM;
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ /* Initialize interface hostname */
+ netif->hostname = "lwip";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ /*
+ * Initialize the snmp variables and counters inside the struct netif.
+ * The last argument should be replaced with your link speed, in units
+ * of bits per second.
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+
+ netif->state = ethernetif;
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+ /* We directly use etharp_output() here to save a function call.
+ * You can instead declare your own function an call etharp_output()
+ * from it if you have to do some checks before sending (e.g. if link
+ * is available...) */
+ netif->output = etharp_output;
+ netif->linkoutput = low_level_output;
+
+ ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
+
+ /* initialize the hardware */
+ low_level_init(netif);
+
+ return ERR_OK;
+}
+
+#endif /* 0 */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/auth.c b/Libraries/Lwip/lwip/src/netif/ppp/auth.c
new file mode 100644
index 00000000..0fd87a37
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/auth.c
@@ -0,0 +1,1334 @@
+/*****************************************************************************
+* auth.c - Network Authentication and Phase Control program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Ported from public pppd code.
+*****************************************************************************/
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_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 <string.h>
+
+#if 0 /* UNUSED */
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+#endif /* UNUSED */
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/* The name by which the peer authenticated itself to us. */
+static char peer_authname[MAXNAMELEN];
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* Set if we have run the /etc/ppp/auth-up script. */
+static int did_authup; /* @todo, we don't need this in lwip*/
+
+/* List of addresses which the peer may use. */
+static struct wordlist *addresses[NUM_PPP];
+
+#if 0 /* UNUSED */
+/* Wordlist giving addresses which the peer may use
+ without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+#endif /* UNUSED */
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+ s_down,
+ s_up
+};
+
+static enum script_state auth_state = s_down;
+static enum script_state auth_script_state = s_down;
+static pid_t auth_script_pid = 0;
+
+/*
+ * Option variables.
+ * lwip: some of these are present in the ppp_settings structure
+ */
+bool uselogin = 0; /* Use /etc/passwd for checking PAP */
+bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */
+bool usehostname = 0; /* Use hostname for our_name */
+bool auth_required = 0; /* Always require authentication from peer */
+bool allow_any_ip = 0; /* Allow peer to use any IP address */
+bool explicit_remote = 0; /* User specified explicit remote name */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+
+#endif /* UNUSED */
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+/* @todo, move this somewhere */
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char word[1];
+};
+
+
+extern char *crypt (const char *, const char *);
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase (int);
+static void check_idle (void *);
+static void connect_time_expired (void *);
+#if 0
+static int plogin (char *, char *, char **, int *);
+#endif
+static void plogout (void);
+static int null_login (int);
+static int get_pap_passwd (int, char *, char *);
+static int have_pap_secret (void);
+static int have_chap_secret (char *, char *, u32_t);
+static int ip_addr_check (u32_t, struct wordlist *);
+
+#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+static int scan_authfile (FILE *, char *, char *, char *,
+ struct wordlist **, struct wordlist **,
+ char *);
+static void free_wordlist (struct wordlist *);
+static void auth_script (char *);
+static void auth_script_done (void *);
+static void set_allowed_addrs (int unit, struct wordlist *addrs);
+static int some_ip_ok (struct wordlist *);
+static int setupapfile (char **);
+static int privgroup (char **);
+static int set_noauth_addr (char **);
+static void check_access (FILE *, char *);
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+ { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "refuse-pap", o_bool, &refuse_pap,
+ "Don't agree to auth to peer with PAP", 1 },
+ { "-pap", o_bool, &refuse_pap,
+ "Don't allow PAP authentication with peer", 1 },
+ { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "refuse-chap", o_bool, &refuse_chap,
+ "Don't agree to auth to peer with CHAP", 1 },
+ { "-chap", o_bool, &refuse_chap,
+ "Don't allow CHAP authentication with peer", 1 },
+ { "name", o_string, our_name,
+ "Set local name for authentication",
+ OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN },
+ { "user", o_string, user,
+ "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN },
+ { "usehostname", o_bool, &usehostname,
+ "Must use hostname for authentication", 1 },
+ { "remotename", o_string, remote_name,
+ "Set remote name for authentication", OPT_STATIC,
+ &explicit_remote, MAXNAMELEN },
+ { "auth", o_bool, &auth_required,
+ "Require authentication from peer", 1 },
+ { "noauth", o_bool, &auth_required,
+ "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip },
+ { "login", o_bool, &uselogin,
+ "Use system password database for PAP", 1 },
+ { "papcrypt", o_bool, &cryptpap,
+ "PAP passwords are encrypted", 1 },
+ { "+ua", o_special, (void *)setupapfile,
+ "Get PAP user and password from file" },
+ { "password", o_string, passwd,
+ "Password for authenticating us to the peer", OPT_STATIC,
+ NULL, MAXSECRETLEN },
+ { "privgroup", o_special, (void *)privgroup,
+ "Allow group members to use privileged options", OPT_PRIV },
+ { "allow-ip", o_special, (void *)set_noauth_addr,
+ "Set IP address(es) which can be used without authentication",
+ OPT_PRIV },
+ { NULL }
+};
+#endif /* UNUSED */
+#if 0 /* UNUSED */
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(char **argv)
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ seteuid(getuid());
+ ufile = fopen(*argv, "r");
+ seteuid(0);
+ if (ufile == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = 0;
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = 0;
+
+ return (1);
+}
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(char **argv)
+{
+ struct group *g;
+ int i;
+
+ g = getgrnam(*argv);
+ if (g == 0) {
+ option_error("group %s is unknown", *argv);
+ return 0;
+ }
+ for (i = 0; i < ngroups; ++i) {
+ if (groups[i] == g->gr_gid) {
+ privileged = 1;
+ break;
+ }
+ }
+ return 1;
+}
+#endif
+
+#if 0 /* UNUSED */
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(char **argv)
+{
+ char *addr = *argv;
+ int l = strlen(addr);
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1);
+ if (wp == NULL)
+ novm("allow-ip argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = noauth_addrs;
+ BCOPY(addr, wp->word, l);
+ noauth_addrs = wp;
+ return 1;
+}
+#endif /* UNUSED */
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+
+ AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit));
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(int unit)
+{
+ AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit));
+ if (lcp_phase[unit] == PHASE_DEAD) {
+ return;
+ }
+ if (logged_in) {
+ plogout();
+ }
+ lcp_phase[unit] = PHASE_DEAD;
+ AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n"));
+ pppLinkTerminated(unit);
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(int unit)
+{
+ int i;
+ struct protent *protp;
+
+ AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit));
+
+ if (did_authup) {
+ /* XXX Do link down processing. */
+ did_authup = 0;
+ }
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag) {
+ continue;
+ }
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) {
+ (*protp->lowerdown)(unit);
+ }
+ if (protp->protocol < 0xC000 && protp->close != NULL) {
+ (*protp->close)(unit, "LCP down");
+ }
+ }
+ num_np_open = 0; /* number of network protocols we have opened */
+ num_np_up = 0; /* Number of network protocols which have come up */
+
+ if (lcp_phase[unit] != PHASE_DEAD) {
+ lcp_phase[unit] = PHASE_TERMINATE;
+ }
+ pppLinkDown(unit);
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(int unit)
+{
+ int auth;
+ int i;
+ struct protent *protp;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+#if PAP_SUPPORT || CHAP_SUPPORT
+ lcp_options *ho = &lcp_hisoptions[unit];
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+ AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit));
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) {
+ (*protp->lowerup)(unit);
+ }
+ }
+ if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * treat it as though it authenticated with PAP using a username
+ * of "" and a password of "". If that's not OK, boot it out.
+ */
+ if (!wo->neg_upap || !null_login(unit)) {
+ AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n"));
+ lcp_close(unit, "peer refused to authenticate");
+ return;
+ }
+ }
+
+ lcp_phase[unit] = PHASE_AUTHENTICATE;
+ auth = 0;
+#if CHAP_SUPPORT
+ if (go->neg_chap) {
+ ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ }
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT && CHAP_SUPPORT
+ else
+#endif /* PAP_SUPPORT && CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (go->neg_upap) {
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (ho->neg_chap) {
+ ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ }
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT && CHAP_SUPPORT
+ else
+#endif /* PAP_SUPPORT && CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (ho->neg_upap) {
+ if (ppp_settings.passwd[0] == 0) {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) {
+ AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n"));
+ }
+ }
+ upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd);
+ auth |= PAP_WITHPEER;
+ }
+#endif /* PAP_SUPPORT */
+ auth_pending[unit] = auth;
+
+ if (!auth) {
+ network_phase(unit);
+ }
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(int unit)
+{
+ int i;
+ struct protent *protp;
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if ((go->neg_chap || go->neg_upap) && !did_authup) {
+ /* XXX Do setup for peer authentication. */
+ did_authup = 1;
+ }
+
+#if CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ lcp_phase[unit] = PHASE_CALLBACK;
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif /* CBCP_SUPPORT */
+
+ lcp_phase[unit] = PHASE_NETWORK;
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) {
+ (*protp->open)(unit);
+ if (protp->protocol != PPP_CCP) {
+ ++num_np_open;
+ }
+ }
+ }
+
+ if (num_np_open == 0) {
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+ }
+}
+/* @todo: add void start_networks(void) here (pppd 2.3.11) */
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(int unit, u16_t protocol)
+{
+ LWIP_UNUSED_ARG(protocol);
+
+ AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol));
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+}
+
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(int unit, u16_t protocol, char *name, int namelen)
+{
+ int pbit;
+
+ AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol));
+ switch (protocol) {
+ case PPP_CHAP:
+ pbit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ pbit = PAP_PEER;
+ break;
+ default:
+ AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > (int)sizeof(peer_authname) - 1) {
+ namelen = sizeof(peer_authname) - 1;
+ }
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = 0;
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~pbit) == 0) {
+ network_phase(unit);
+ }
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(int unit, u16_t protocol)
+{
+ int errCode = PPPERR_AUTHFAIL;
+
+ LWIP_UNUSED_ARG(protocol);
+
+ AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol));
+ if (passwd_from_file) {
+ BZERO(ppp_settings.passwd, MAXSECRETLEN);
+ }
+
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * He'll probably take the link down, and there's not much
+ * we can do except wait for that.
+ */
+ pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode);
+ lcp_close(unit, "Failed to authenticate ourselves to peer");
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(int unit, u16_t protocol)
+{
+ int pbit;
+
+ AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol));
+ switch (protocol) {
+ case PPP_CHAP:
+ pbit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file) {
+ BZERO(ppp_settings.passwd, MAXSECRETLEN);
+ }
+ pbit = PAP_WITHPEER;
+ break;
+ default:
+ AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
+ pbit = 0;
+ }
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~pbit) == 0) {
+ network_phase(unit);
+ }
+}
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto));
+ if (num_np_up == 0) {
+ AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit));
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ if (ppp_settings.idle_time_limit > 0) {
+ TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit);
+ }
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (ppp_settings.maxconnect > 0) {
+ TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect);
+ }
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto));
+ if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) {
+ UNTIMEOUT(check_idle, NULL);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto));
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(void *arg)
+{
+ struct ppp_idle idle;
+ u_short itime;
+
+ LWIP_UNUSED_ARG(arg);
+ if (!get_idle_time(0, &idle)) {
+ return;
+ }
+ itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
+ if (itime >= ppp_settings.idle_time_limit) {
+ /* link is idle: shut it down. */
+ AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n"));
+ lcp_close(0, "Link inactive");
+ } else {
+ TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ AUTHDEBUG(LOG_INFO, ("Connect time expired\n"));
+ lcp_close(0, "Connect time expired"); /* Close connection */
+}
+
+#if 0 /* UNUSED */
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options(void)
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u32_t remote;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) {
+ strcpy(ppp_settings.our_name, ppp_settings.hostname);
+ }
+
+ if (ppp_settings.user[0] == 0) {
+ strcpy(ppp_settings.user, ppp_settings.our_name);
+ }
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ can_auth = wo->neg_upap && have_pap_secret();
+ if (!can_auth && wo->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote);
+ }
+
+ if (ppp_settings.auth_required && !can_auth) {
+ ppp_panic("No auth secret");
+ }
+}
+#endif /* UNUSED */
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(int unit)
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u32_t remote;
+
+ AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit));
+ ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL));
+ ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/;
+
+ if (go->neg_upap && !have_pap_secret()) {
+ go->neg_upap = 0;
+ }
+ if (go->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) {
+ go->neg_chap = 0;
+ }
+ }
+}
+
+#if PAP_SUPPORT
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+u_char
+check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen)
+{
+#if 1 /* XXX Assume all entries OK. */
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(auser);
+ LWIP_UNUSED_ARG(userlen);
+ LWIP_UNUSED_ARG(apasswd);
+ LWIP_UNUSED_ARG(passwdlen);
+ LWIP_UNUSED_ARG(msglen);
+ *msg = (char *) 0;
+ return UPAP_AUTHACK; /* XXX Assume all entries OK. */
+#else
+ u_char ret = 0;
+ struct wordlist *addrs = NULL;
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static u_short attempts = 0;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ */
+ BCOPY(apasswd, passwd, passwdlen);
+ passwd[passwdlen] = '\0';
+ BCOPY(auser, user, userlen);
+ user[userlen] = '\0';
+ *msg = (char *) 0;
+
+ /* XXX Validate user name and password. */
+ ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */
+
+ if (ret == UPAP_AUTHNAK) {
+ if (*msg == (char *) 0) {
+ *msg = "Login incorrect";
+ }
+ *msglen = strlen(*msg);
+ /*
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user));
+ /*ppp_panic("Excess Bad Logins");*/
+ }
+ if (attempts > 3) {
+ /* @todo: this was sleep(), i.e. seconds, not milliseconds
+ * I don't think we really need this in lwIP - we would block tcpip_thread!
+ */
+ /*sys_msleep((attempts - 3) * 5);*/
+ }
+ if (addrs != NULL) {
+ free_wordlist(addrs);
+ }
+ } else {
+ attempts = 0; /* Reset count */
+ if (*msg == (char *) 0) {
+ *msg = "Login ok";
+ }
+ *msglen = strlen(*msg);
+ set_allowed_addrs(unit, addrs);
+ }
+
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+#endif
+}
+#endif /* PAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+
+/* lwip does not support PAM*/
+
+#endif /* USE_PAM */
+
+#endif /* UNUSED */
+
+
+#if 0 /* UNUSED */
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+static int
+plogin(char *user, char *passwd, char **msg, int *msglen)
+{
+
+ LWIP_UNUSED_ARG(user);
+ LWIP_UNUSED_ARG(passwd);
+ LWIP_UNUSED_ARG(msg);
+ LWIP_UNUSED_ARG(msglen);
+
+
+ /* The new lines are here align the file when
+ * compared against the pppd 2.3.11 code */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /* XXX Fail until we decide that we want to support logins. */
+ return (UPAP_AUTHNAK);
+}
+#endif
+
+
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout(void)
+{
+ logged_in = 0;
+}
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+ /* XXX Fail until we decide that we want to support logins. */
+ return 0;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ */
+static int
+get_pap_passwd(int unit, char *user, char *passwd)
+{
+ LWIP_UNUSED_ARG(unit);
+/* normally we would reject PAP if no password is provided,
+ but this causes problems with some providers (like CHT in Taiwan)
+ who incorrectly request PAP and expect a bogus/empty password, so
+ always provide a default user/passwd of "none"/"none"
+
+ @todo: This should be configured by the user, instead of being hardcoded here!
+*/
+ if(user) {
+ strcpy(user, "none");
+ }
+ if(passwd) {
+ strcpy(passwd, "none");
+ }
+ return 1;
+}
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(void)
+{
+ /* XXX Fail until we set up our passwords. */
+ return 0;
+}
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'. Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(char *client, char *server, u32_t remote)
+{
+ LWIP_UNUSED_ARG(client);
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(remote);
+
+ /* XXX Fail until we set up our passwords. */
+ return 0;
+}
+#if CHAP_SUPPORT
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs)
+{
+#if 1
+ int len;
+ struct wordlist *addrs;
+
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(save_addrs);
+
+ addrs = NULL;
+
+ if(!client || !client[0] || strcmp(client, ppp_settings.user)) {
+ return 0;
+ }
+
+ len = (int)strlen(ppp_settings.passwd);
+ if (len > MAXSECRETLEN) {
+ AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ len = MAXSECRETLEN;
+ }
+
+ BCOPY(ppp_settings.passwd, secret, len);
+ *secret_len = len;
+
+ return 1;
+#else
+ int ret = 0, len;
+ struct wordlist *addrs;
+ char secbuf[MAXWORDLEN];
+
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ /* XXX Find secret. */
+ if (ret < 0) {
+ return 0;
+ }
+
+ if (save_addrs) {
+ set_allowed_addrs(unit, addrs);
+ }
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ len = MAXSECRETLEN;
+ }
+
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+#endif
+}
+#endif /* CHAP_SUPPORT */
+
+
+#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ */
+static void
+set_allowed_addrs(int unit, struct wordlist *addrs)
+{
+ if (addresses[unit] != NULL) {
+ free_wordlist(addresses[unit]);
+ }
+ addresses[unit] = addrs;
+
+#if 0
+ /*
+ * If there's only one authorized address we might as well
+ * ask our peer for that one right away
+ */
+ if (addrs != NULL && addrs->next == NULL) {
+ char *p = addrs->word;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u32_t a;
+ struct hostent *hp;
+
+ if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) {
+ hp = gethostbyname(p);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u32_t *)hp->h_addr;
+ } else {
+ a = inet_addr(p);
+ }
+ if (a != (u32_t) -1) {
+ wo->hisaddr = a;
+ }
+ }
+ }
+#endif
+}
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(int unit, u32_t addr)
+{
+ return ip_addr_check(addr, addresses[unit]);
+}
+
+static int /* @todo: integrate this funtion into auth_ip_addr()*/
+ip_addr_check(u32_t addr, struct wordlist *addrs)
+{
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr)) {
+ return 0;
+ }
+
+ if (addrs == NULL) {
+ return !ppp_settings.auth_required; /* no addresses authorized */
+ }
+
+ /* XXX All other addresses allowed. */
+ return 1;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(u32_t addr)
+{
+ addr = ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(struct wordlist *addrs)
+{
+ for (; addrs != 0; addrs = addrs->next) {
+ if (addrs->word[0] == '-')
+ break;
+ if (addrs->word[0] != '!')
+ return 1; /* some IP address is allowed */
+ }
+ return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(FILE *f, char *filename)
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ warn("cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ warn("Warning - secret file %s has world and/or group access",
+ filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1
+ * if no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs. Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ */
+static int
+scan_authfile(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename)
+{
+ /* We do not (currently) need this in lwip */
+ return 0; /* dummy */
+}
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(struct wordlist *wp)
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+static void
+auth_script_done(void *arg)
+{
+ auth_script_pid = 0;
+ switch (auth_script_state) {
+ case s_up:
+ if (auth_state == s_down) {
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ break;
+ case s_down:
+ if (auth_state == s_up) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ break;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(char *script)
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[8];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ slprintf(struid, sizeof(struid), "%d", getuid());
+ user_name = struid;
+ }
+ slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+}
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/auth.h b/Libraries/Lwip/lwip/src/netif/ppp/auth.h
new file mode 100644
index 00000000..a8069ec4
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/auth.h
@@ -0,0 +1,111 @@
+/*****************************************************************************
+* auth.h - PPP Authentication and phase control header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original derived from BSD pppd.h.
+*****************************************************************************/
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef AUTH_H
+#define AUTH_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/* we are starting to use the link */
+void link_required (int);
+
+/* we are finished with the link */
+void link_terminated (int);
+
+/* the LCP layer has left the Opened state */
+void link_down (int);
+
+/* the link is up; authenticate now */
+void link_established (int);
+
+/* a network protocol has come up */
+void np_up (int, u16_t);
+
+/* a network protocol has gone down */
+void np_down (int, u16_t);
+
+/* a network protocol no longer needs link */
+void np_finished (int, u16_t);
+
+/* peer failed to authenticate itself */
+void auth_peer_fail (int, u16_t);
+
+/* peer successfully authenticated itself */
+void auth_peer_success (int, u16_t, char *, int);
+
+/* we failed to authenticate ourselves */
+void auth_withpeer_fail (int, u16_t);
+
+/* we successfully authenticated ourselves */
+void auth_withpeer_success (int, u16_t);
+
+/* check authentication options supplied */
+void auth_check_options (void);
+
+/* check what secrets we have */
+void auth_reset (int);
+
+/* Check peer-supplied username/password */
+u_char check_passwd (int, char *, int, char *, int, char **, int *);
+
+/* get "secret" for chap */
+int get_secret (int, char *, char *, char *, int *, int);
+
+/* check if IP address is authorized */
+int auth_ip_addr (int, u32_t);
+
+/* check if IP address is unreasonable */
+int bad_ip_adrs (u32_t);
+
+#endif /* AUTH_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/chap.c b/Libraries/Lwip/lwip/src/netif/ppp/chap.c
new file mode 100644
index 00000000..f10e27d2
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/chap.c
@@ -0,0 +1,908 @@
+/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/
+/*****************************************************************************
+* chap.c - Network Challenge Handshake Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD chap.c.
+*****************************************************************************/
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "magic.h"
+#include "randm.h"
+#include "auth.h"
+#include "md5.h"
+#include "chap.h"
+#include "chpms.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+ { "chap-restart", o_int, &chap[0].timeouttime,
+ "Set timeout for CHAP" },
+ { "chap-max-challenge", o_int, &chap[0].max_transmits,
+ "Set max #xmits for challenge" },
+ { "chap-interval", o_int, &chap[0].chal_interval,
+ "Set interval for rechallenge" },
+#ifdef MSLANMAN
+ { "ms-lanman", o_bool, &ms_lanman,
+ "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+ { NULL }
+};
+#endif /* UNUSED */
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit (int);
+static void ChapLowerUp (int);
+static void ChapLowerDown (int);
+static void ChapInput (int, u_char *, int);
+static void ChapProtocolReject (int);
+#if PPP_ADDITIONAL_CALLBACKS
+static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *);
+#endif
+
+struct protent chap_protent = {
+ PPP_CHAP,
+ ChapInit,
+ ChapInput,
+ ChapProtocolReject,
+ ChapLowerUp,
+ ChapLowerDown,
+ NULL,
+ NULL,
+#if PPP_ADDITIONAL_CALLBACKS
+ ChapPrintPkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "CHAP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout (void *);
+static void ChapResponseTimeout (void *);
+static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int);
+static void ChapRechallenge (void *);
+static void ChapReceiveResponse (chap_state *, u_char *, int, int);
+static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len);
+static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len);
+static void ChapSendStatus (chap_state *, int);
+static void ChapSendChallenge (chap_state *);
+static void ChapSendResponse (chap_state *);
+static void ChapGenChallenge (chap_state *);
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(int unit, char *our_name, u_char digest)
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(int unit, char *our_name, u_char digest)
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE) {
+ return;
+ }
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n"));
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ return;
+ }
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN) {
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL) {
+ cstate->clientstate = CHAPCS_CLOSED;
+ } else if (cstate->clientstate == CHAPCS_PENDING) {
+ cstate->clientstate = CHAPCS_LISTEN;
+ }
+
+ if (cstate->serverstate == CHAPSS_INITIAL) {
+ cstate->serverstate = CHAPSS_CLOSED;
+ } else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE) {
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ } else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0) {
+ UNTIMEOUT(ChapRechallenge, cstate);
+ }
+ if (cstate->clientstate == CHAPCS_RESPONSE) {
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+ }
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED) {
+ auth_peer_fail(unit, PPP_CHAP);
+ }
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED) {
+ auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
+ }
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(int unit, u_char *inpacket, int packet_len)
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n"));
+ return;
+ }
+ if (len > packet_len) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n"));
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default: /* Need code reject? */
+ CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code));
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[256];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id));
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= (int)sizeof(rhostname)) {
+ len = sizeof(rhostname) - 1;
+ }
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n",
+ rhostname));
+
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) {
+ strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname));
+ rhostname[sizeof(rhostname) - 1] = 0;
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n",
+ rhostname));
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n",
+ rhostname));
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE) {
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+ }
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, (u_char*)secret, secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#if MSCHAP_SUPPORT
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type));
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len)
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[256];
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id));
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n",
+ cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id) {
+ return; /* doesn't match ID of last challenge */
+ }
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= (int)sizeof(rhostname)) {
+ len = sizeof(rhostname) - 1;
+ }
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n",
+ rhostname));
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
+ secret, &secret_len, 1)) {
+ CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n",
+ rhostname));
+ } else {
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE) {
+ break; /* it's not even the right length */
+ }
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, (u_char*)secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) {
+ code = CHAP_SUCCESS; /* they are the same! */
+ }
+ break;
+
+ default:
+ CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type));
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0) {
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ }
+ } else {
+ CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n"));
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(inp);
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id));
+
+ if (cstate->clientstate == CHAPCS_OPEN) {
+ /* presumably an answer to a duplicate response */
+ return;
+ }
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0) {
+ PRINTMSG(inp, len);
+ }
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(inp);
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id));
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0) {
+ PRINTMSG(inp, len);
+ }
+
+ CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n"));
+ auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(chap_state *cstate)
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = (int)strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id));
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(chap_state *cstate, int code)
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256]; /* @todo: this can be a char*, no strcpy needed */
+
+ if (code == CHAP_SUCCESS) {
+ strcpy(msg, "Welcome!");
+ } else {
+ strcpy(msg, "I don't like you. Go 'way.");
+ }
+ msglen = (int)strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code,
+ cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(chap_state *cstate)
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned)
+ ((((magic() >> 16) *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16)
+ + MIN_CHALLENGE_LENGTH);
+ LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff);
+ cstate->chal_len = (u_char)chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++ ) {
+ *ptr++ = (char) (magic() & 0xff);
+ }
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(chap_state *cstate)
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = (int)strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static int
+ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN) {
+ return 0;
+ }
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen) {
+ return 0;
+ }
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) {
+ printer(arg, " %s", ChapCodenames[code-1]);
+ } else {
+ printer(arg, " code=0x%x", code);
+ }
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1) {
+ break;
+ }
+ clen = p[0];
+ if (len < clen + 1) {
+ break;
+ }
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = %.*Z", nlen, p);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " %.*Z", len, p);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* CHAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/chap.h b/Libraries/Lwip/lwip/src/netif/ppp/chap.h
new file mode 100644
index 00000000..fedcab8d
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/chap.h
@@ -0,0 +1,150 @@
+/*****************************************************************************
+* chap.h - Network Challenge Handshake Authentication Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original built from BSD network code.
+******************************************************************************/
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.h,v 1.6 2010/01/24 13:19:34 goldsimon Exp $
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 32
+#define MAX_CHALLENGE_LENGTH 64
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ char *resp_name; /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer (int, char *, u_char);
+void ChapAuthPeer (int, char *, u_char);
+
+extern struct protent chap_protent;
+
+#endif /* CHAP_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/chpms.c b/Libraries/Lwip/lwip/src/netif/ppp/chpms.c
new file mode 100644
index 00000000..81a887b8
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/chpms.c
@@ -0,0 +1,396 @@
+/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/
+/*** The original PPPD code is written in a way to require either the UNIX DES
+ encryption functions encrypt(3) and setkey(3) or the DES library libdes.
+ Since both is not included in lwIP, MSCHAP currently does not work! */
+/*****************************************************************************
+* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD chap_ms.c.
+*****************************************************************************/
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#define USE_CRYPT
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "md4.h"
+#ifndef USE_CRYPT
+#include "des.h"
+#endif
+#include "chap.h"
+#include "chpms.h"
+
+#include <string.h>
+
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+
+/************************/
+/*** LOCAL DATA TYPES ***/
+/************************/
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+
+/***********************************/
+/*** LOCAL FUNCTION DECLARATIONS ***/
+/***********************************/
+
+/* XXX Don't know what to do with these. */
+extern void setkey(const char *);
+extern void encrypt(char *, int);
+
+static void DesEncrypt (u_char *, u_char *, u_char *);
+static void MakeKey (u_char *, u_char *);
+
+#ifdef USE_CRYPT
+static void Expand (u_char *, u_char *);
+static void Collapse (u_char *, u_char *);
+#endif
+
+static void ChallengeResponse(
+ u_char *challenge, /* IN 8 octets */
+ u_char *pwHash, /* IN 16 octets */
+ u_char *response /* OUT 24 octets */
+);
+static void ChapMS_NT(
+ char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response
+);
+static u_char Get7Bits(
+ u_char *input,
+ int startBit
+);
+
+static void
+ChallengeResponse( u_char *challenge, /* IN 8 octets */
+ u_char *pwHash, /* IN 16 octets */
+ u_char *response /* OUT 24 octets */)
+{
+ u_char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, 16);
+
+#if 0
+ log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt( u_char *clear, /* IN 8 octets */
+ u_char *key, /* IN 7 octets */
+ u_char *cipher /* OUT 8 octets */)
+{
+ u_char des_key[8];
+ u_char crypt_key[66];
+ u_char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey((char*)crypt_key);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ Expand(clear, des_input);
+ encrypt((char*)des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt( u_char *clear, /* IN 8 octets */
+ u_char *key, /* IN 7 octets */
+ u_char *cipher /* OUT 8 octets */)
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char
+Get7Bits( u_char *input, int startBit)
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void
+Expand(u_char *in, u_char *out)
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--) {
+ *out++ = (c >> j) & 01;
+ }
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void
+Collapse(u_char *in, u_char *out)
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++) {
+ c |= *in << j;
+ }
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void
+MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */
+ u_char *des_key /* OUT 64 bit DES key with parity bits added */)
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n",
+ key[0], key[1], key[2], key[3], key[4], key[5], key[6]));
+ CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7]));
+#endif
+}
+
+static void
+ChapMS_NT( char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response)
+{
+ int i;
+ MDstruct md4Context;
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ static int low_byte_first = -1;
+
+ LWIP_UNUSED_ARG(rchallenge_len);
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++) {
+ unicodePassword[i * 2] = (u_char)secret[i];
+ }
+ MDbegin(&md4Context);
+ MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */
+
+ if (low_byte_first == -1) {
+ low_byte_first = (PP_HTONS((unsigned short int)1) != 1);
+ }
+ if (low_byte_first == 0) {
+ /* @todo: arg type - u_long* or u_int* ? */
+ MDreverse((unsigned int*)&md4Context); /* sfb 961105 */
+ }
+
+ MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */
+
+ ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan( char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response)
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[16];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++) {
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+ }
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len)
+{
+ MS_ChapResponse response;
+#ifdef MSLANMAN
+ extern int ms_lanman;
+#endif
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+#endif /* MSCHAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/chpms.h b/Libraries/Lwip/lwip/src/netif/ppp/chpms.h
new file mode 100644
index 00000000..df070fb3
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/chpms.h
@@ -0,0 +1,64 @@
+/*****************************************************************************
+* chpms.h - Network Microsoft Challenge Handshake Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-01-30 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original built from BSD network code.
+******************************************************************************/
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $
+ */
+
+#ifndef CHPMS_H
+#define CHPMS_H
+
+#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
+
+void ChapMS (chap_state *, char *, int, char *, int);
+
+#endif /* CHPMS_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/fsm.c b/Libraries/Lwip/lwip/src/netif/ppp/fsm.c
new file mode 100644
index 00000000..e8a254ed
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/fsm.c
@@ -0,0 +1,890 @@
+/*****************************************************************************
+* fsm.c - Network Control Protocol Finite State Machine program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD fsm.c.
+*****************************************************************************/
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+
+#include <string.h>
+
+#if PPP_DEBUG
+static const char *ppperr_strerr[] = {
+ "LS_INITIAL", /* LS_INITIAL 0 */
+ "LS_STARTING", /* LS_STARTING 1 */
+ "LS_CLOSED", /* LS_CLOSED 2 */
+ "LS_STOPPED", /* LS_STOPPED 3 */
+ "LS_CLOSING", /* LS_CLOSING 4 */
+ "LS_STOPPING", /* LS_STOPPING 5 */
+ "LS_REQSENT", /* LS_REQSENT 6 */
+ "LS_ACKRCVD", /* LS_ACKRCVD 7 */
+ "LS_ACKSENT", /* LS_ACKSENT 8 */
+ "LS_OPENED" /* LS_OPENED 9 */
+};
+#endif /* PPP_DEBUG */
+
+static void fsm_timeout (void *);
+static void fsm_rconfreq (fsm *, u_char, u_char *, int);
+static void fsm_rconfack (fsm *, int, u_char *, int);
+static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
+static void fsm_rtermreq (fsm *, int, u_char *, int);
+static void fsm_rtermack (fsm *);
+static void fsm_rcoderej (fsm *, u_char *, int);
+static void fsm_sconfreq (fsm *, int);
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(fsm *f)
+{
+ f->state = LS_INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = FSM_DEFTIMEOUT;
+ f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
+ f->maxtermtransmits = FSM_DEFMAXTERMREQS;
+ f->maxnakloops = FSM_DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_INITIAL:
+ f->state = LS_CLOSED;
+ break;
+
+ case LS_STARTING:
+ if( f->flags & OPT_SILENT ) {
+ f->state = LS_STOPPED;
+ } else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_CLOSED:
+ f->state = LS_INITIAL;
+ break;
+
+ case LS_STOPPED:
+ f->state = LS_STARTING;
+ if( f->callbacks->starting ) {
+ (*f->callbacks->starting)(f);
+ }
+ break;
+
+ case LS_CLOSING:
+ f->state = LS_INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case LS_STOPPING:
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ f->state = LS_STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case LS_OPENED:
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f);
+ }
+ f->state = LS_STARTING;
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_INITIAL:
+ f->state = LS_STARTING;
+ if( f->callbacks->starting ) {
+ (*f->callbacks->starting)(f);
+ }
+ break;
+
+ case LS_CLOSED:
+ if( f->flags & OPT_SILENT ) {
+ f->state = LS_STOPPED;
+ } else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ }
+ break;
+
+ case LS_CLOSING:
+ f->state = LS_STOPPING;
+ /* fall through */
+ case LS_STOPPED:
+ case LS_OPENED:
+ if( f->flags & OPT_RESTART ) {
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+#if 0 /* backport pppd 2.4.4b1; */
+/*
+ * terminate_layer - Start process of shutting down the FSM
+ *
+ * Cancel any timeout running, notify upper layers we're done, and
+ * send a terminate-request message as configured.
+ */
+static void
+terminate_layer(fsm *f, int nextstate)
+{
+ /* @todo */
+}
+#endif
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the LS_CLOSED state.
+ */
+void
+fsm_close(fsm *f, char *reason)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason));
+ switch( f->state ) {
+ case LS_STARTING:
+ f->state = LS_INITIAL;
+ break;
+ case LS_STOPPED:
+ f->state = LS_CLOSED;
+ break;
+ case LS_STOPPING:
+ f->state = LS_CLOSING;
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ case LS_OPENED:
+ if( f->state != LS_OPENED ) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ } else if( f->callbacks->down ) {
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+ }
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = LS_CLOSING;
+ break;
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(void *arg)
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case LS_CLOSING:
+ case LS_STOPPING:
+ if( f->retransmits <= 0 ) {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ } else {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ if (f->retransmits <= 0) {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ f->state = LS_STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ } else {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit) {
+ (*f->callbacks->retransmit)(f);
+ }
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == LS_ACKRCVD ) {
+ f->state = LS_REQSENT;
+ }
+ }
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(fsm *f, u_char *inpacket, int l)
+{
+ u_char *inp = inpacket;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ if (l < HEADERLEN) {
+ FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n",
+ f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n",
+ f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n",
+ f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n",
+ f->protocol, f->state, ppperr_strerr[f->state]));
+ return;
+ }
+ FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CONFNAK:
+ case CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f)));
+ if( !f->callbacks->extcode ||
+ !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ }
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
+{
+ int code, reject_if_disagree;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+ switch( f->state ) {
+ case LS_CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case LS_CLOSING:
+ case LS_STOPPING:
+ return;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ break;
+
+ case LS_STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci) { /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len) {
+ code = CONFREJ; /* Reject all CI */
+ } else {
+ code = CONFACK;
+ }
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, (u_char)code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == LS_ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = LS_OPENED;
+ if (f->callbacks->up) {
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ }
+ } else {
+ f->state = LS_ACKSENT;
+ }
+ f->nakloops = 0;
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != LS_ACKRCVD) {
+ f->state = LS_REQSENT;
+ }
+ if( code == CONFNAK ) {
+ ++f->nakloops;
+ }
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(fsm *f, int id, u_char *inp, int len)
+{
+ FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ if (id != f->reqid || f->seen_ack) { /* Expected id? */
+ return; /* Nope, toss... */
+ }
+ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
+ /* Ack is bad - ignore it */
+ FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n",
+ PROTO_NAME(f), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case LS_CLOSED:
+ case LS_STOPPED:
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+ break;
+
+ case LS_REQSENT:
+ f->state = LS_ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case LS_ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = LS_OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up) {
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ }
+ break;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
+{
+ int (*proc) (fsm *, u_char *, int);
+ int ret;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ if (id != f->reqid || f->seen_ack) { /* Expected id? */
+ return; /* Nope, toss... */
+ }
+ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (!proc || !((ret = proc(f, inp, len)))) {
+ /* Nak/reject is bad - ignore it */
+ FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n",
+ PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case LS_CLOSED:
+ case LS_STOPPED:
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0) {
+ f->state = LS_STOPPED; /* kludge for stopping CCP */
+ } else {
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ }
+ break;
+
+ case LS_ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(fsm *f, int id, u_char *p, int len)
+{
+ LWIP_UNUSED_ARG(p);
+
+ FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ switch (f->state) {
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ f->state = LS_REQSENT; /* Start over but keep trying */
+ break;
+
+ case LS_OPENED:
+ if (len > 0) {
+ FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p));
+ } else {
+ FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f)));
+ }
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ f->retransmits = 0;
+ f->state = LS_STOPPING;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(fsm *f)
+{
+ FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+
+ switch (f->state) {
+ case LS_CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = LS_CLOSED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_ACKRCVD:
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_OPENED:
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0);
+ break;
+ default:
+ FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(fsm *f, u_char *inp, int len)
+{
+ u_char code, id;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+
+ if (len < HEADERLEN) {
+ FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n",
+ PROTO_NAME(f), code, id));
+
+ if( f->state == LS_ACKRCVD ) {
+ f->state = LS_REQSENT;
+ }
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(fsm *f)
+{
+ switch( f->state ) {
+ case LS_CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case LS_CLOSED:
+ f->state = LS_CLOSED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_STOPPING:
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case LS_STOPPED:
+ f->state = LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_OPENED:
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f);
+ }
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = LS_STOPPING;
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(fsm *f, int retransmit)
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci ) {
+ (*f->callbacks->resetci)(f);
+ }
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ) {
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
+ if( f->callbacks->cilen && f->callbacks->addci ) {
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ }
+ if (f->callbacks->addci) {
+ (*f->callbacks->addci)(f, outp, &cilen);
+ }
+ } else {
+ cilen = 0;
+ }
+
+ /* send the request to our peer */
+ fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+
+ FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n",
+ PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
+{
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf[f->unit];
+ if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ }
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ }
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
+ FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n",
+ PROTO_NAME(f), code, id, outlen));
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/fsm.h b/Libraries/Lwip/lwip/src/netif/ppp/fsm.h
new file mode 100644
index 00000000..8d41b5f5
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/fsm.h
@@ -0,0 +1,157 @@
+/*****************************************************************************
+* fsm.h - Network Control Protocol Finite State Machine header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original based on BSD code.
+*****************************************************************************/
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.h,v 1.5 2009/12/31 17:08:08 goldsimon Exp $
+ */
+
+#ifndef FSM_H
+#define FSM_H
+
+/*
+ * LCP Packet header = Code, id, length.
+ */
+#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ 1 /* Configuration Request */
+#define CONFACK 2 /* Configuration Ack */
+#define CONFNAK 3 /* Configuration Nak */
+#define CONFREJ 4 /* Configuration Reject */
+#define TERMREQ 5 /* Termination Request */
+#define TERMACK 6 /* Termination Ack */
+#define CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ u_short protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks* callbacks; /* Callback routines */
+ char* term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci)(fsm*); /* Reset our Configuration Information */
+ int (*cilen)(fsm*); /* Length of our Configuration Information */
+ void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */
+ int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */
+ int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */
+ int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */
+ int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */
+ void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */
+ void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */
+ void (*starting)(fsm*); /* Called when we want the lower layer */
+ void (*finished)(fsm*); /* Called when we don't want the lower layer */
+ void (*protreject)(int); /* Called when Protocol-Reject received */
+ void (*retransmit)(fsm*); /* Retransmission is necessary */
+ int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */
+ char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define LS_INITIAL 0 /* Down, hasn't been opened */
+#define LS_STARTING 1 /* Down, been opened */
+#define LS_CLOSED 2 /* Up, hasn't been opened */
+#define LS_STOPPED 3 /* Open, waiting for down event */
+#define LS_CLOSING 4 /* Terminating the connection, not open */
+#define LS_STOPPING 5 /* Terminating, but open */
+#define LS_REQSENT 6 /* We've sent a Config Request */
+#define LS_ACKRCVD 7 /* We've received a Config Ack */
+#define LS_ACKSENT 8 /* We've sent a Config Ack */
+#define LS_OPENED 9 /* Connection available */
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init (fsm*);
+void fsm_lowerup (fsm*);
+void fsm_lowerdown (fsm*);
+void fsm_open (fsm*);
+void fsm_close (fsm*, char*);
+void fsm_input (fsm*, u_char*, int);
+void fsm_protreject (fsm*);
+void fsm_sdata (fsm*, u_char, u_char, u_char*, int);
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
+
+#endif /* FSM_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/ipcp.c b/Libraries/Lwip/lwip/src/netif/ppp/ipcp.c
new file mode 100644
index 00000000..f0ab2e0e
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/ipcp.c
@@ -0,0 +1,1411 @@
+/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and
+ dial-on-demand has been stripped. */
+/*****************************************************************************
+* ipcp.c - Network PPP IP Control Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "auth.h"
+#include "fsm.h"
+#include "vj.h"
+#include "ipcp.h"
+
+#include "lwip/inet.h"
+
+#include <string.h>
+
+/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+/* local vars */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci (fsm *); /* Reset our CI */
+static int ipcp_cilen (fsm *); /* Return length of our CI */
+static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */
+static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */
+static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */
+static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */
+static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipcp_up (fsm *); /* We're UP */
+static void ipcp_down (fsm *); /* We're DOWN */
+#if PPP_ADDITIONAL_CALLBACKS
+static void ipcp_script (fsm *, char *); /* Run an up/down script */
+#endif
+static void ipcp_finished (fsm *); /* Don't need lower layer */
+
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches LS_OPENED state */
+ ipcp_down, /* Called when fsm leaves LS_OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init (int);
+static void ipcp_open (int);
+static void ipcp_close (int, char *);
+static void ipcp_lowerup (int);
+static void ipcp_lowerdown (int);
+static void ipcp_input (int, u_char *, int);
+static void ipcp_protrej (int);
+
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+#if PPP_ADDITIONAL_CALLBACKS
+ ipcp_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "IPCP",
+#if PPP_ADDITIONAL_CALLBACKS
+ ip_check_options,
+ NULL,
+ ip_active_pkt
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+static void ipcp_clear_addrs (int);
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(int unit)
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->ouraddr = 0;
+#if VJ_SUPPORT
+ wo->neg_vj = 1;
+#else /* VJ_SUPPORT */
+ wo->neg_vj = 0;
+#endif /* VJ_SUPPORT */
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_SLOTS - 1;
+ wo->cflag = 0;
+ wo->default_route = 1;
+
+ ao->neg_addr = 1;
+#if VJ_SUPPORT
+ ao->neg_vj = 1;
+#else /* VJ_SUPPORT */
+ ao->neg_vj = 0;
+#endif /* VJ_SUPPORT */
+ ao->maxslotindex = MAX_SLOTS - 1;
+ ao->cflag = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(int unit)
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(int unit, char *reason)
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(int unit)
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(int unit)
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(int unit, u_char *p, int len)
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(int unit)
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(fsm *f)
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0) {
+ wo->accept_local = 1;
+ }
+ if (wo->hisaddr == 0) {
+ wo->accept_remote = 1;
+ }
+ /* Request DNS addresses from the peer */
+ wo->req_dns1 = ppp_settings.usepeerdns;
+ wo->req_dns2 = ppp_settings.usepeerdns;
+ ipcp_gotoptions[f->unit] = *wo;
+ cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(fsm *f)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ if (cis_received[f->unit] == 0) {
+ /* keep trying the new style until we see some CI from the peer */
+ go->neg_vj = 1;
+ } else {
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj) +
+ LENCIDNS(go->req_dns1) +
+ LENCIDNS(go->req_dns2));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(fsm *f, u_char *ucp, int *lenp)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+#define ADDCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = ntohl(addr); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETSHORT(cishort, p); \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) { \
+ goto bad; \
+ } \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) { \
+ goto bad; \
+ } \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u32_t l; \
+ if ((len -= addrlen) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) { \
+ goto bad; \
+ } \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) { \
+ goto bad; \
+ } \
+ } \
+ }
+
+#define ACKCIDNS(opt, neg, addr) \
+ if (neg) { \
+ u32_t l; \
+ if ((len -= CILEN_ADDR) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_ADDR || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (addr != cilong) { \
+ goto bad; \
+ } \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ return (1);
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_ackci: received bad Ack!\n"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u32_t ciaddr1, ciaddr2, l, cidnsaddr;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else { \
+ ciaddr2 = 0; \
+ } \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIDNS(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cidnsaddr = htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ IPCPDEBUG(LOG_INFO, ("local IP address %s\n",
+ inet_ntoa(ciaddr1)));
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ IPCPDEBUG(LOG_INFO, ("remote IP address %s\n",
+ inet_ntoa(ciaddr2)));
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex) {
+ try.maxslotindex = cimaxslotindex;
+ }
+ if (!cicflag) {
+ try.cflag = 0;
+ }
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ NAKCIDNS(CI_MS_DNS1, req_dns1,
+ try.dnsaddr[0] = cidnsaddr;
+ IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr)));
+ );
+
+ NAKCIDNS(CI_MS_DNS2, req_dns2,
+ try.dnsaddr[1] = cidnsaddr;
+ IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr)));
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 ) {
+ goto bad;
+ }
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ goto bad;
+ }
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS) {
+ goto bad;
+ }
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local) {
+ try.ouraddr = ciaddr1;
+ }
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote) {
+ try.hisaddr = ciaddr2;
+ }
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) {
+ goto bad;
+ }
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local) {
+ try.ouraddr = ciaddr1;
+ }
+ if (try.ouraddr != 0) {
+ try.neg_addr = 1;
+ }
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0) {
+ goto bad;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) { \
+ goto bad; \
+ } \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) { \
+ goto bad; \
+ } \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) { \
+ goto bad; \
+ } \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) { \
+ goto bad; \
+ } \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIDNS(opt, neg, dnsaddr) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != dnsaddr) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+ REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ /*
+ * Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+ return 1;
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_rejci: received bad Reject!\n"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree)
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+#ifdef OLD_CI_ADDRS
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+#endif
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u32_t tl, ciaddr1; /* Parsed address values */
+#ifdef OLD_CI_ADDRS
+ u32_t ciaddr2; /* Parsed address values */
+#endif
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ cis_received[f->unit] = 1;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = (u_short)l;/* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+#ifdef OLD_CI_ADDRS /* Need to save space... */
+ case CI_ADDRS:
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n"));
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2)));
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+#endif
+
+ case CI_ADDR:
+ if (!ao->neg_addr) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ } else if (cilen != CILEN_ADDR) { /* Check CI length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1)));
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1)));
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1)));
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n",
+ d+1, inet_ntoa(tl)));
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1));
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ if (!ao->neg_vj) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen));
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort));
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex));
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag));
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_SLOTS - 1;
+ ho->cflag = 1;
+ }
+ IPCPDEBUG(LOG_INFO, (
+ "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n",
+ ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag));
+ break;
+
+ default:
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) { /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+ }
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) { /* Getting fed up with sending NAKs? */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n"));
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) { /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ }
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip) {
+ BCOPY(cip, ucp, cilen); /* Move it */
+ }
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n"));
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = (int)(ucp - inp); /* Compute output length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+#if 0
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options(u_long localAddr)
+{
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Load our default IP address but allow the remote host to give us
+ * a new address.
+ */
+ if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) {
+ wo->accept_local = 1; /* don't insist on this default value */
+ wo->ouraddr = htonl(localAddr);
+ }
+}
+#endif
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(fsm *f)
+{
+ u32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ np_up(f->unit, PPP_IP);
+ IPCPDEBUG(LOG_INFO, ("ipcp: up\n"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr) {
+ ho->hisaddr = wo->hisaddr;
+ }
+
+ if (ho->hisaddr == 0) {
+ IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n"));
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n"));
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+
+ if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+ /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/
+ }
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n",
+ inet_ntoa(ho->hisaddr)));
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ /* set tcp compression */
+ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) {
+ IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ IPCPDEBUG(LOG_WARNING, ("sifup failed\n"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route) {
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) {
+ default_route_set[f->unit] = 1;
+ }
+ }
+
+ IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr)));
+ IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr)));
+ if (go->dnsaddr[0]) {
+ IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0])));
+ }
+ if (go->dnsaddr[1]) {
+ IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1])));
+ }
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(fsm *f)
+{
+ IPCPDEBUG(LOG_INFO, ("ipcp: down\n"));
+ np_down(f->unit, PPP_IP);
+ sifvjcomp(f->unit, 0, 0, 0);
+
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit);
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes, etc.
+ */
+static void
+ipcp_clear_addrs(int unit)
+{
+ u32_t ouraddr, hisaddr;
+
+ ouraddr = ipcp_gotoptions[unit].ouraddr;
+ hisaddr = ipcp_hisoptions[unit].hisaddr;
+ if (default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(fsm *f)
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static int
+ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(plen);
+ LWIP_UNUSED_ARG(printer);
+ LWIP_UNUSED_ARG(arg);
+ return 0;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(u_char *pkt, int len)
+{
+ u_char *tcp;
+ int hlen;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN) {
+ return 0;
+ }
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0) {
+ return 0;
+ }
+ if (get_ipproto(pkt) != IPPROTO_TCP) {
+ return 1;
+ }
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN) {
+ return 0;
+ }
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) {
+ return 0;
+ }
+ return 1;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/ipcp.h b/Libraries/Lwip/lwip/src/netif/ppp/ipcp.h
new file mode 100644
index 00000000..de03f460
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/ipcp.h
@@ -0,0 +1,106 @@
+/*****************************************************************************
+* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $
+ */
+
+#ifndef IPCP_H
+#define IPCP_H
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS1 128 /* Primary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#define CI_MS_WINS2 130 /* Secondary WINS value */
+
+#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
+ /* maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */
+#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
+ /* compression option */
+
+typedef struct ipcp_options {
+ u_int neg_addr : 1; /* Negotiate IP Address? */
+ u_int old_addrs : 1; /* Use old (IP-Addresses) option? */
+ u_int req_addr : 1; /* Ask peer to send IP address? */
+ u_int default_route : 1; /* Assign default route through interface? */
+ u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */
+ u_int neg_vj : 1; /* Van Jacobson Compression? */
+ u_int old_vj : 1; /* use old (short) form of VJ option? */
+ u_int accept_local : 1; /* accept peer's value for ouraddr */
+ u_int accept_remote : 1; /* accept peer's value for hisaddr */
+ u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */
+ u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ u_char maxslotindex; /* VJ slots - 1. */
+ u_char cflag; /* VJ slot compression flag. */
+ u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+ u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+ u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+extern struct protent ipcp_protent;
+
+#endif /* IPCP_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/lcp.c b/Libraries/Lwip/lwip/src/netif/ppp/lcp.c
new file mode 100644
index 00000000..54f758aa
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/lcp.c
@@ -0,0 +1,2066 @@
+/*****************************************************************************
+* lcp.c - Network Link Control Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+#include "chap.h"
+#include "magic.h"
+#include "auth.h"
+#include "lcp.h"
+
+#include <string.h>
+
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#else
+#define PPPOE_MAXMTU PPP_MAXMRU
+#endif
+
+#if 0 /* UNUSED */
+/*
+ * LCP-related command-line options.
+ */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+bool lax_recv = 0; /* accept control chars in asyncmap */
+
+static int setescape (char **);
+
+static option_t lcp_option_list[] = {
+ /* LCP options */
+ /* list stripped for simplicity */
+ {NULL}
+};
+#endif /* UNUSED */
+
+/* options */
+LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
+static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */
+static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */
+
+/* global vars */
+static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */
+
+static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static u32_t lcp_echo_number = 0; /* ID number of next echo frame */
+static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */
+
+/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */
+static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci (fsm*); /* Reset our CI */
+static int lcp_cilen (fsm*); /* Return length of our CI */
+static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */
+static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */
+static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */
+static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */
+static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */
+static void lcp_up (fsm*); /* We're UP */
+static void lcp_down (fsm*); /* We're DOWN */
+static void lcp_starting (fsm*); /* We need lower layer up */
+static void lcp_finished (fsm*); /* We need lower layer down */
+static int lcp_extcode (fsm*, int, u_char, u_char*, int);
+static void lcp_rprotrej (fsm*, u_char*, int);
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup (int);
+static void lcp_echo_lowerdown (int);
+static void LcpEchoTimeout (void*);
+static void lcp_received_echo_reply (fsm*, int, u_char*, int);
+static void LcpSendEchoRequest (fsm*);
+static void LcpLinkFailure (fsm*);
+static void LcpEchoCheck (fsm*);
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches LS_OPENED state */
+ lcp_down, /* Called when fsm leaves LS_OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_input (int, u_char *, int);
+static void lcp_protrej (int);
+
+struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+#if PPP_ADDITIONAL_CALLBACKS
+ lcp_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "LCP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */
+#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */
+#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ")
+
+#if 0 /* UNUSED */
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'", p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+#endif /* UNUSED */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+void
+lcp_init(int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ wo->passive = 0;
+ wo->silent = 0;
+ wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */
+ wo->neg_mru = 1;
+ wo->mru = PPP_DEFMRU;
+ wo->neg_asyncmap = 1;
+ wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
+ wo->neg_chap = 0; /* Set to 1 on server */
+ wo->neg_upap = 0; /* Set to 1 on server */
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+ wo->neg_lqr = 0; /* no LQR implementation yet */
+ wo->neg_cbcp = 0;
+
+ ao->neg_mru = 1;
+ ao->mru = PPP_MAXMRU;
+ ao->neg_asyncmap = 1;
+ ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
+ ao->neg_chap = (CHAP_SUPPORT != 0);
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = (PAP_SUPPORT != 0);
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+ ao->neg_lqr = 0; /* no LQR implementation yet */
+ ao->neg_cbcp = (CBCP_SUPPORT != 0);
+
+ /*
+ * Set transmit escape for the flag and escape characters plus anything
+ * set for the allowable options.
+ */
+ memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+ xmit_accm[unit][15] = 0x60;
+ xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF));
+ xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF);
+ xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF);
+ xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF);
+ LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n",
+ xmit_accm[unit][0],
+ xmit_accm[unit][1],
+ xmit_accm[unit][2],
+ xmit_accm[unit][3]));
+
+ lcp_phase[unit] = PHASE_INITIALIZE;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags = 0;
+ if (wo->passive) {
+ f->flags |= OPT_PASSIVE;
+ }
+ if (wo->silent) {
+ f->flags |= OPT_SILENT;
+ }
+ fsm_open(f);
+
+ lcp_phase[unit] = PHASE_ESTABLISH;
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(int unit, char *reason)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_phase[unit] != PHASE_DEAD) {
+ lcp_phase[unit] = PHASE_TERMINATE;
+ }
+ if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do an
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = LS_CLOSED;
+ lcp_finished(f);
+ } else {
+ fsm_close(f, reason);
+ }
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(int unit)
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_set_xaccm(unit, &xmit_accm[unit]);
+ ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0);
+ ppp_recv_config(unit, PPP_MRU, 0x00000000l,
+ wo->neg_pcompression, wo->neg_accompression);
+ peer_mru[unit] = PPP_MRU;
+ lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0]
+ | ((u_long)xmit_accm[unit][1] << 8)
+ | ((u_long)xmit_accm[unit][2] << 16)
+ | ((u_long)xmit_accm[unit][3] << 24);
+ LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n",
+ xmit_accm[unit][3],
+ xmit_accm[unit][2],
+ xmit_accm[unit][1],
+ xmit_accm[unit][0]));
+
+ fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(int unit)
+{
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(int unit, u_char *p, int len)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len)
+{
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != LS_OPENED) {
+ break;
+ }
+ LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id));
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(fsm *f, u_char *inp, int len)
+{
+ int i;
+ struct protent *protp;
+ u_short prot;
+
+ if (len < (int)sizeof (u_short)) {
+ LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n"));
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot));
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * LS_OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != LS_OPENED ) {
+ LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state));
+ return;
+ }
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol == prot && protp->enabled_flag) {
+ (*protp->protrej)(f->unit);
+ return;
+ }
+ }
+
+ LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot));
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+static void
+lcp_protrej(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+ /*
+ * Can't reject LCP!
+ */
+ LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n"));
+ fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(int unit, u_char *p, int len)
+{
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the LS_OPENED state.
+ */
+
+ fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(fsm *f)
+{
+ lcp_wantoptions[f->unit].magicnumber = magic();
+ lcp_wantoptions[f->unit].numloops = 0;
+ lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
+ peer_mru[f->unit] = PPP_MRU;
+ auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(fsm *f)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP and UPAP, even if we will
+ * accept either.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) +
+ LENCICHAP(go->neg_chap) +
+ LENCISHORT(!go->neg_chap && go->neg_upap) +
+ LENCILQR(go->neg_lqr) +
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(fsm *f, u_char *ucp, int *lenp)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#define ADDCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(val, ucp); \
+ PUTCHAR(digest, ucp); \
+ }
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n"));
+ }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+lcp_ackci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cilen, citype, cichar;
+ u_short cishort;
+ u32_t cilong;
+
+ /*
+ * CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || citype != opt) \
+ goto bad; \
+ }
+#define ACKCISHORT(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+#define ACKCICHAR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_CHAR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR || citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != val) \
+ goto bad; \
+ }
+#define ACKCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ if ((len -= CILEN_CHAP) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAP || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != digest) \
+ goto bad; \
+ }
+#define ACKCILONG(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LONG) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LONG || citype != opt) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCILQR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LQR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LQR || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != PPP_LQR) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n"));
+ return (1);
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_acki: received bad Ack!\n"));
+ return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+lcp_nakci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ u_char citype, cichar, *next;
+ u_short cishort;
+ u32_t cilong;
+ lcp_options no; /* options we've seen Naks for */
+ lcp_options try; /* options to request next time */
+ int looped_back = 0;
+ int cilen;
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIVOID(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAP(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCISHORT(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILONG(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILQR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * We don't care if they want to send us smaller packets than
+ * we want. Therefore, accept any MRU less than what we asked for,
+ * but then ignore the new value when setting the MRU in the kernel.
+ * If they send us a bigger MRU than what we asked, accept it, up to
+ * the limit of the default MRU we'd get if we didn't negotiate.
+ */
+ if (go->neg_mru && go->mru != PPP_DEFMRU) {
+ NAKCISHORT(CI_MRU, neg_mru,
+ if (cishort <= wo->mru || cishort < PPP_DEFMRU) {
+ try.mru = cishort;
+ }
+ );
+ }
+
+ /*
+ * Add any characters they want to our (receive-side) asyncmap.
+ */
+ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) {
+ NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+ try.asyncmap = go->asyncmap | cilong;
+ );
+ }
+
+ /*
+ * If they've nak'd our authentication-protocol, check whether
+ * they are proposing a different protocol, or a different
+ * hash algorithm for CHAP.
+ */
+ if ((go->neg_chap || go->neg_upap)
+ && len >= CILEN_SHORT
+ && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+ no.neg_chap = go->neg_chap;
+ no.neg_upap = go->neg_upap;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+ /*
+ * If we were asking for CHAP, they obviously don't want to do it.
+ * If we weren't asking for CHAP, then we were asking for PAP,
+ * in which case this Nak is bad.
+ */
+ if (!go->neg_chap) {
+ goto bad;
+ }
+ try.neg_chap = 0;
+
+ } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+ GETCHAR(cichar, p);
+ if (go->neg_chap) {
+ /*
+ * We were asking for CHAP/MD5; they must want a different
+ * algorithm. If they can't do MD5, we'll have to stop
+ * asking for CHAP.
+ */
+ if (cichar != go->chap_mdtype) {
+ try.neg_chap = 0;
+ }
+ } else {
+ /*
+ * Stop asking for PAP if we were asking for it.
+ */
+ try.neg_upap = 0;
+ }
+
+ } else {
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ if (go->neg_chap) {
+ try.neg_chap = 0;
+ } else {
+ try.neg_upap = 0;
+ }
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+ /*
+ * If they can't cope with our link quality protocol, we'll have
+ * to stop asking for LQR. We haven't got any other protocol.
+ * If they Nak the reporting period, take their value XXX ?
+ */
+ NAKCILQR(CI_QUALITY, neg_lqr,
+ if (cishort != PPP_LQR) {
+ try.neg_lqr = 0;
+ } else {
+ try.lqr_period = cilong;
+ }
+ );
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try.neg_cbcp = 0;
+ );
+
+ /*
+ * Check for a looped-back line.
+ */
+ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+ try.magicnumber = magic();
+ looped_back = 1;
+ );
+
+ /*
+ * Peer shouldn't send Nak for protocol compression or
+ * address/control compression requests; they should send
+ * a Reject instead. If they send a Nak, treat it as a Reject.
+ */
+ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
+ try.neg_pcompression = 0;
+ );
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
+ try.neg_accompression = 0;
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If we see an option that we requested, or one we've already seen
+ * in this packet, then this packet is bad.
+ * If we wanted to respond by starting to negotiate on the requested
+ * option(s), we could, but we don't, because except for the
+ * authentication type and quality protocol, if we are not negotiating
+ * an option, it is because we were told not to.
+ * For the authentication type, the Nak from the peer means
+ * `let me authenticate myself with you' which is a bit pointless.
+ * For the quality protocol, the Nak means `ask me to send you quality
+ * reports', but if we didn't ask for them, we don't want them.
+ * An option we don't recognize represents the peer asking to
+ * negotiate some option we don't support, so ignore it.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (cilen < CILEN_VOID || (len -= cilen) < 0) {
+ goto bad;
+ }
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_MRU:
+ if ((go->neg_mru && go->mru != PPP_DEFMRU)
+ || no.neg_mru || cilen != CILEN_SHORT) {
+ goto bad;
+ }
+ GETSHORT(cishort, p);
+ if (cishort < PPP_DEFMRU) {
+ try.mru = cishort;
+ }
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl)
+ || no.neg_asyncmap || cilen != CILEN_LONG) {
+ goto bad;
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) {
+ goto bad;
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (go->neg_magicnumber || no.neg_magicnumber ||
+ cilen != CILEN_LONG) {
+ goto bad;
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (go->neg_pcompression || no.neg_pcompression
+ || cilen != CILEN_VOID) {
+ goto bad;
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (go->neg_accompression || no.neg_accompression
+ || cilen != CILEN_VOID) {
+ goto bad;
+ }
+ break;
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) {
+ goto bad;
+ }
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0) {
+ goto bad;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ if (looped_back) {
+ if (++try.numloops >= lcp_loopbackfail) {
+ LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n"));
+ lcp_close(f->unit, "Loopback detected");
+ }
+ } else {
+ try.numloops = 0;
+ }
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_nakci: received bad Nak!\n"));
+ return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int
+lcp_rejci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cichar;
+ u_short cishort;
+ u32_t cilong;
+ lcp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \
+ }
+#define REJCISHORT(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \
+ }
+#define REJCICHAP(opt, neg, val, digest) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cishort != val || cichar != digest) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ try.neg_upap = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \
+ }
+#define REJCILONG(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \
+ }
+#define REJCILQR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cishort != PPP_LQR || cilong != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \
+ }
+#define REJCICBCP(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CBCP && \
+ p[1] == CILEN_CBCP && \
+ p[0] == opt) { \
+ len -= CILEN_CBCP; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+ REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
+ if (!go->neg_chap) {
+ REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+ }
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ /*
+ * Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+ return 1;
+
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_rejci: received bad Reject!\n"));
+ return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(fsm *f,
+ u_char *inp, /* Requested CIs */
+ int *lenp, /* Length of requested CIs */
+ int reject_if_disagree)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ int cilen, citype; /* Parsed len, type */
+ u_char cichar; /* Parsed char value */
+ u_short cishort; /* Parsed short value */
+ u32_t cilong; /* Parse long value */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *rejp; /* Pointer to next char in reject frame */
+ u_char *nakp; /* Pointer to next char in Nak frame */
+ int l = *lenp; /* Length left */
+#if TRACELCP > 0
+ char traceBuf[80];
+ size_t traceNdx = 0;
+#endif
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ nakp = nak_buffer;
+ rejp = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ citype = 0;
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_MRU:
+ if (!ao->neg_mru) { /* Allow option? */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ } else if (cilen != CILEN_SHORT) { /* Check CI length */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETSHORT(cishort, p); /* Parse MRU */
+
+ /*
+ * He must be able to receive at least our minimum.
+ * No need to check a maximum. If he sends a large number,
+ * we'll just ignore it.
+ */
+ if (cishort < PPP_MINMRU) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n"));
+ orc = CONFNAK; /* Nak CI */
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */
+ break;
+ }
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+
+ case CI_ASYNCMAP:
+ if (!ao->neg_asyncmap) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_LONG) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n"));
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+
+ /*
+ * Asyncmap must have set at least the bits
+ * which are set in lcp_allowoptions[unit].asyncmap.
+ */
+ if ((ao->asyncmap & ~cilong) != 0) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n",
+ cilong, ao->asyncmap));
+ orc = CONFNAK;
+ PUTCHAR(CI_ASYNCMAP, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(ao->asyncmap | cilong, nakp);
+ break;
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+
+ case CI_AUTHTYPE:
+ if (cilen < CILEN_SHORT) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n"));
+ orc = CONFREJ;
+ break;
+ } else if (!(ao->neg_upap || ao->neg_chap)) {
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n"));
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ /*
+ * Authtype must be UPAP or CHAP.
+ *
+ * Note: if both ao->neg_upap and ao->neg_chap are set,
+ * and the peer sends a Configure-Request with two
+ * authenticate-protocol requests, one for CHAP and one
+ * for UPAP, then we will reject the second request.
+ * Whether we end up doing CHAP or UPAP depends then on
+ * the ordering of the CIs in the peer's Configure-Request.
+ */
+
+ if (cishort == PPP_PAP) {
+ if (ho->neg_chap) { /* we've already accepted CHAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_SHORT) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n"));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_upap) { /* we don't want to do PAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n"));
+ orc = CONFNAK; /* NAK it and suggest CHAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+ ho->neg_upap = 1;
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+ }
+ if (cishort == PPP_CHAP) {
+ if (ho->neg_upap) { /* we've already accepted PAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_CHAP) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n"));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_chap) { /* we don't want to do CHAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n"));
+ orc = CONFNAK; /* NAK it and suggest PAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ break;
+ }
+ GETCHAR(cichar, p); /* get digest type*/
+ if (cichar != CHAP_DIGEST_MD5
+#if MSCHAP_SUPPORT
+ && cichar != CHAP_MICROSOFT
+#endif
+ ) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar));
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar);
+ traceNdx = strlen(traceBuf);
+#endif
+ ho->chap_mdtype = cichar; /* save md type */
+ ho->neg_chap = 1;
+ break;
+ }
+
+ /*
+ * We don't recognize the protocol they're asking for.
+ * Nak it with something we're willing to do.
+ * (At this point we know ao->neg_upap || ao->neg_chap.)
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ if (ao->neg_chap) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort));
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ } else {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort));
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ }
+ break;
+
+ case CI_QUALITY:
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+
+ if (!ao->neg_lqr ||
+ cilen != CILEN_LQR) {
+ orc = CONFREJ;
+ break;
+ }
+
+ /*
+ * Check the protocol and the reporting period.
+ * XXX When should we Nak this, and what with?
+ */
+ if (cishort != PPP_LQR) {
+ orc = CONFNAK;
+ PUTCHAR(CI_QUALITY, nakp);
+ PUTCHAR(CILEN_LQR, nakp);
+ PUTSHORT(PPP_LQR, nakp);
+ PUTLONG(ao->lqr_period, nakp);
+ break;
+ }
+ break;
+
+ case CI_MAGICNUMBER:
+ if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+
+ /*
+ * He must have a different magic number.
+ */
+ if (go->neg_magicnumber &&
+ cilong == go->magicnumber) {
+ cilong = magic(); /* Don't put magic() inside macro! */
+ orc = CONFNAK;
+ PUTCHAR(CI_MAGICNUMBER, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(cilong, nakp);
+ break;
+ }
+ ho->neg_magicnumber = 1;
+ ho->magicnumber = cilong;
+ break;
+
+
+ case CI_PCOMPRESSION:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION");
+ traceNdx = strlen(traceBuf);
+#endif
+ if (!ao->neg_pcompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION");
+ traceNdx = strlen(traceBuf);
+#endif
+ if (!ao->neg_accompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_accompression = 1;
+ break;
+
+ case CI_MRRU:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ case CI_SSNHF:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ case CI_EPDISC:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ default:
+#if TRACELCP
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype);
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+ }
+
+ endswitch:
+#if TRACELCP
+ if (traceNdx >= 80 - 32) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf));
+ traceNdx = 0;
+ }
+#endif
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) { /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+ }
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree /* Getting fed up with sending NAKs? */
+ && citype != CI_MAGICNUMBER) {
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) { /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ }
+ rc = CONFNAK;
+ }
+ }
+ if (orc == CONFREJ) { /* Reject this CI */
+ rc = CONFREJ;
+ if (cip != rejp) { /* Need to move rejected CI? */
+ BCOPY(cip, rejp, cilen); /* Move it */
+ }
+ INCPTR(cilen, rejp); /* Update output pointer */
+ }
+ }
+
+ /*
+ * If we wanted to send additional NAKs (for unsent CIs), the
+ * code would go here. The extra NAKs would go at *nakp.
+ * At present there are no cases where we want to ask the
+ * peer to negotiate an option.
+ */
+
+ switch (rc) {
+ case CONFACK:
+ *lenp = (int)(next - inp);
+ break;
+ case CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+ */
+ *lenp = (int)(nakp - nak_buffer);
+ BCOPY(nak_buffer, inp, *lenp);
+ break;
+ case CONFREJ:
+ *lenp = (int)(rejp - inp);
+ break;
+ }
+
+#if TRACELCP > 0
+ if (traceNdx > 0) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf));
+ }
+#endif
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(fsm *f)
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+
+ if (!go->neg_magicnumber) {
+ go->magicnumber = 0;
+ }
+ if (!ho->neg_magicnumber) {
+ ho->magicnumber = 0;
+ }
+
+ /*
+ * Set our MTU to the smaller of the MTU we wanted and
+ * the MRU our peer wanted. If we negotiated an MRU,
+ * set our MRU to the larger of value we wanted and
+ * the value we got in the negotiation.
+ */
+ ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)),
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl),
+ ho->neg_pcompression, ho->neg_accompression);
+ /*
+ * If the asyncmap hasn't been negotiated, we really should
+ * set the receive asyncmap to ffffffff, but we set it to 0
+ * for backwards contemptibility.
+ */
+ ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU),
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ go->neg_pcompression, go->neg_accompression);
+
+ if (ho->neg_mru) {
+ peer_mru[f->unit] = ho->mru;
+ }
+
+ lcp_echo_lowerup(f->unit); /* Enable echo messages */
+
+ link_established(f->unit); /* The link is up; authenticate now */
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(fsm *f)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+ lcp_echo_lowerdown(f->unit);
+
+ link_down(f->unit);
+
+ ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0);
+ ppp_recv_config(f->unit, PPP_MRU,
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ go->neg_pcompression, go->neg_accompression);
+ peer_mru[f->unit] = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(fsm *f)
+{
+ link_required(f->unit); /* lwip: currently does nothing */
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(fsm *f)
+{
+ link_terminated(f->unit); /* we are finished with the link */
+}
+
+
+#if PPP_ADDITIONAL_CALLBACKS
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+static void
+print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg)
+{
+ int c;
+
+ printer(arg, "\"");
+ for (; len > 0; --len) {
+ c = *p++;
+ if (' ' <= c && c <= '~') {
+ if (c == '\\' || c == '"') {
+ printer(arg, "\\");
+ }
+ printer(arg, "%c", c);
+ } else {
+ switch (c) {
+ case '\n':
+ printer(arg, "\\n");
+ break;
+ case '\r':
+ printer(arg, "\\r");
+ break;
+ case '\t':
+ printer(arg, "\\t");
+ break;
+ default:
+ printer(arg, "\\%.3o", c);
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static char *lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq"
+};
+
+static int
+lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u32_t cilong;
+
+ if (plen < HEADERLEN) {
+ return 0;
+ }
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen) {
+ return 0;
+ }
+
+ if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) {
+ printer(arg, " %s", lcp_codenames[code-1]);
+ } else {
+ printer(arg, " code=0x%x", code);
+ }
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_MRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mru %d", cishort);
+ }
+ break;
+ case CI_ASYNCMAP:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "asyncmap 0x%lx", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+ case PPP_CHAP:
+ printer(arg, "chap");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_QUALITY:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "quality ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_LQR:
+ printer(arg, "lqr");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case CBCP_OPT:
+ printer(arg, "CBCP");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "magic 0x%x", cilong);
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "pcomp");
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "accomp");
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char*)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case ECHOREQ:
+ case ECHOREP:
+ case DISCREQ:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ p += 4;
+ len -= 4;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return (int)(p - pstart);
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+static void
+LcpLinkFailure (fsm *f)
+{
+ if (f->state == LS_OPENED) {
+ LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending));
+ LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n"));
+ lcp_close(f->unit, "Peer not responding");
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+static void
+LcpEchoCheck (fsm *f)
+{
+ LcpSendEchoRequest (f);
+
+ /*
+ * Start the timer for the next interval.
+ */
+ LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0);
+
+ TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+ lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+static void
+LcpEchoTimeout (void *arg)
+{
+ if (lcp_echo_timer_running != 0) {
+ lcp_echo_timer_running = 0;
+ LcpEchoCheck ((fsm *) arg);
+ }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+static void
+lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len)
+{
+ u32_t magic;
+
+ LWIP_UNUSED_ARG(id);
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len));
+ return;
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) {
+ LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n"));
+ return;
+ }
+
+ /* Reset the number of outstanding echo frames */
+ lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+static void
+LcpSendEchoRequest (fsm *f)
+{
+ u32_t lcp_magic;
+ u_char pkt[4], *pktp;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if (lcp_echo_fails != 0) {
+ if (lcp_echos_pending >= lcp_echo_fails) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ }
+ }
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == LS_OPENED) {
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt));
+ ++lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (lcp_echo_interval != 0) {
+ LcpEchoCheck (f);
+ }
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/lcp.h b/Libraries/Lwip/lwip/src/netif/ppp/lcp.h
new file mode 100644
index 00000000..b9201eeb
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/lcp.h
@@ -0,0 +1,151 @@
+/*****************************************************************************
+* lcp.h - Network Link Control Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $
+ */
+
+#ifndef LCP_H
+#define LCP_H
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_CALLBACK 13 /* callback */
+#define CI_MRRU 17 /* max reconstructed receive unit; multilink */
+#define CI_SSNHF 18 /* short sequence numbers for multilink */
+#define CI_EPDISC 19 /* endpoint discriminator */
+
+/*
+ * LCP-specific packet types (code numbers).
+ */
+#define PROTREJ 8 /* Protocol Reject */
+#define ECHOREQ 9 /* Echo Request */
+#define ECHOREP 10 /* Echo Reply */
+#define DISCREQ 11 /* Discard Request */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ u_int passive : 1; /* Don't die if we don't get a response */
+ u_int silent : 1; /* Wait for the other end to start first */
+ u_int restart : 1; /* Restart vs. exit after close */
+ u_int neg_mru : 1; /* Negotiate the MRU? */
+ u_int neg_asyncmap : 1; /* Negotiate the async map? */
+ u_int neg_upap : 1; /* Ask for UPAP authentication? */
+ u_int neg_chap : 1; /* Ask for CHAP authentication? */
+ u_int neg_magicnumber : 1; /* Ask for magic number? */
+ u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */
+ u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
+ u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */
+ u_int neg_cbcp : 1; /* Negotiate use of CBCP */
+#ifdef PPP_MULTILINK
+ u_int neg_mrru : 1; /* Negotiate multilink MRRU */
+ u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */
+ u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */
+#endif
+ u_short mru; /* Value of MRU */
+#ifdef PPP_MULTILINK
+ u_short mrru; /* Value of MRRU, and multilink enable */
+#endif
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u32_t asyncmap; /* Value of async map */
+ u32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+#ifdef PPP_MULTILINK
+ struct epdisc endpoint; /* endpoint discriminator */
+#endif
+} lcp_options;
+
+/*
+ * Values for phase from BSD pppd.h based on RFC 1661.
+ */
+typedef enum {
+ PHASE_DEAD = 0,
+ PHASE_INITIALIZE,
+ PHASE_ESTABLISH,
+ PHASE_AUTHENTICATE,
+ PHASE_CALLBACK,
+ PHASE_NETWORK,
+ PHASE_TERMINATE
+} LinkPhase;
+
+
+
+extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+extern ext_accm xmit_accm[];
+
+
+void lcp_init (int);
+void lcp_open (int);
+void lcp_close (int, char *);
+void lcp_lowerup (int);
+void lcp_lowerdown(int);
+void lcp_sprotrej (int, u_char *, int); /* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
+
+#endif /* LCP_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/magic.c b/Libraries/Lwip/lwip/src/netif/ppp/magic.c
new file mode 100644
index 00000000..3732a424
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/magic.c
@@ -0,0 +1,80 @@
+/*****************************************************************************
+* magic.c - Network Random Number Generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD magic.c.
+*****************************************************************************/
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT
+
+#include "ppp_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/Libraries/Lwip/lwip/src/netif/ppp/magic.h b/Libraries/Lwip/lwip/src/netif/ppp/magic.h
new file mode 100644
index 00000000..eba70d20
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/magic.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+* magic.h - Network Random Number Generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: magic.h,v 1.3 2010/01/18 20:49:43 goldsimon Exp $
+ */
+
+#ifndef MAGIC_H
+#define MAGIC_H
+
+/* Initialize the magic number generator */
+void magicInit(void);
+
+/* Returns the next magic number */
+u32_t magic(void);
+
+#endif /* MAGIC_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/md5.c b/Libraries/Lwip/lwip/src/netif/ppp/md5.c
new file mode 100644
index 00000000..dc3cc751
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/md5.c
@@ -0,0 +1,320 @@
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if CHAP_SUPPORT || MD5_SUPPORT
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "md5.h"
+
+#include <string.h>
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform (u32_t *buf, u32_t *in);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+#ifdef __STDC__
+#define UL(x) x##UL
+#else
+#ifdef WIN32
+#define UL(x) x##UL
+#else
+#define UL(x) x
+#endif
+#endif
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void
+MD5Init (MD5_CTX *mdContext)
+{
+ mdContext->i[0] = mdContext->i[1] = (u32_t)0;
+
+ /* Load magic initialization constants. */
+ mdContext->buf[0] = (u32_t)0x67452301UL;
+ mdContext->buf[1] = (u32_t)0xefcdab89UL;
+ mdContext->buf[2] = (u32_t)0x98badcfeUL;
+ mdContext->buf[3] = (u32_t)0x10325476UL;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void
+MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen)
+{
+ u32_t in[16];
+ int mdi;
+ unsigned int i, ii;
+
+#if 0
+ PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf));
+ PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf));
+#endif
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) {
+ mdContext->i[1]++;
+ }
+ mdContext->i[0] += ((u32_t)inLen << 3);
+ mdContext->i[1] += ((u32_t)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4) {
+ in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
+ (((u32_t)mdContext->in[ii+2]) << 16) |
+ (((u32_t)mdContext->in[ii+1]) << 8) |
+ ((u32_t)mdContext->in[ii]);
+ }
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+void
+MD5Final (unsigned char hash[], MD5_CTX *mdContext)
+{
+ u32_t in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4) {
+ in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
+ (((u32_t)mdContext->in[ii+2]) << 16) |
+ (((u32_t)mdContext->in[ii+1]) << 8) |
+ ((u32_t)mdContext->in[ii]);
+ }
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+ SMEMCPY(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void
+Transform (u32_t *buf, u32_t *in)
+{
+ u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+ FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+ FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+ FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+ FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+ FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+ FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+ FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+ GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+ GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */
+ GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+ GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+ GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+ GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+ HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+ HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+ HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+ HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+ HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+ HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+ II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+ II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+ II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+ II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+ II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+ II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif /* CHAP_SUPPORT || MD5_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/md5.h b/Libraries/Lwip/lwip/src/netif/ppp/md5.h
new file mode 100644
index 00000000..e129533f
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/md5.h
@@ -0,0 +1,55 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ u32_t i[2]; /* number of _bits_ handled mod 2^64 */
+ u32_t buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ( MD5_CTX *mdContext);
+void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
+void MD5Final ( unsigned char hash[], MD5_CTX *mdContext);
+
+#endif /* MD5_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/pap.c b/Libraries/Lwip/lwip/src/netif/ppp/pap.c
new file mode 100644
index 00000000..5fb9f886
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/pap.c
@@ -0,0 +1,628 @@
+/*****************************************************************************
+* pap.c - Network Password Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-12 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "auth.h"
+#include "pap.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+static bool hide_password = 1;
+
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+ { "hide-password", o_bool, &hide_password,
+ "Don't output passwords to log", 1 },
+ { "show-password", o_bool, &hide_password,
+ "Show password string in debug log messages", 0 },
+ { "pap-restart", o_int, &upap[0].us_timeouttime,
+ "Set retransmit timeout for PAP" },
+ { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+ "Set max number of transmissions for auth-reqs" },
+ { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+ "Set time limit for peer PAP authentication" },
+ { NULL }
+};
+#endif
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init (int);
+static void upap_lowerup (int);
+static void upap_lowerdown (int);
+static void upap_input (int, u_char *, int);
+static void upap_protrej (int);
+#if PPP_ADDITIONAL_CALLBACKS
+static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+#if PPP_ADDITIONAL_CALLBACKS
+ upap_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "PAP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout (void *);
+static void upap_reqtimeout(void *);
+static void upap_rauthreq (upap_state *, u_char *, u_char, int);
+static void upap_rauthack (upap_state *, u_char *, int, int);
+static void upap_rauthnak (upap_state *, u_char *, int, int);
+static void upap_sauthreq (upap_state *);
+static void upap_sresp (upap_state *, u_char, u_char, char *, int);
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit));
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(int unit, char *user, char *password)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n",
+ unit, user, password, u->us_clientstate));
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = (int)strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = (int)strlen(password);
+
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0) {
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(void *arg)
+{
+ upap_state *u = (upap_state *) arg;
+
+ UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n",
+ u->us_unit, u->us_timeouttime, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) {
+ UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n"));
+ return;
+ }
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n"));
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(void *arg)
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN) {
+ return; /* huh?? */
+ }
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate));
+
+ if (u->us_clientstate == UPAPCS_INITIAL) {
+ u->us_clientstate = UPAPCS_CLOSED;
+ } else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ /* now client state is UPAPCS__AUTHREQ */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL) {
+ u->us_serverstate = UPAPSS_CLOSED;
+ } else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0) {
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate));
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) {
+ UNTIMEOUT(upap_reqtimeout, u);
+ }
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n"));
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n"));
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(int unit, u_char *inpacket, int l)
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < (int)UPAP_HEADERLEN) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < (int)UPAP_HEADERLEN) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n"));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n"));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len));
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len)
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ u_char retcode;
+ char *msg;
+ int msglen;
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id));
+
+ if (u->us_serverstate < UPAPSS_LISTEN) {
+ return;
+ }
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < (int)sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen);
+ /* lwip: currently retcode is always UPAP_AUTHACK */
+ BZERO(rpasswd, rpasswdlen);
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0) {
+ UNTIMEOUT(upap_reqtimeout, u);
+ }
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(upap_state *u, u_char *inp, int id, int len)
+{
+ u_char msglen;
+ char *msg;
+
+ LWIP_UNUSED_ARG(id);
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n"));
+ return;
+ }
+
+ /*
+ * Parse message.
+ */
+ if (len < (int)sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n"));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n"));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nak.
+ */
+static void
+upap_rauthnak(upap_state *u, u_char *inp, int id, int len)
+{
+ u_char msglen;
+ char *msg;
+
+ LWIP_UNUSED_ARG(id);
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
+ return;
+ }
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n"));
+ } else {
+ GETCHAR(msglen, inp);
+ if(msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n"));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n"));
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(upap_state *u)
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char)
+ + u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf[u->us_unit];
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
+
+ UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id));
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen)
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf[u->us_unit];
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
+
+ UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate));
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static int upap_printpkt(
+ u_char *p,
+ int plen,
+ void (*printer) (void *, char *, ...),
+ void *arg
+)
+{
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(plen);
+ LWIP_UNUSED_ARG(printer);
+ LWIP_UNUSED_ARG(arg);
+ return 0;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* PAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/pap.h b/Libraries/Lwip/lwip/src/netif/ppp/pap.h
new file mode 100644
index 00000000..c99a2040
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/pap.h
@@ -0,0 +1,118 @@
+/*****************************************************************************
+* pap.h - PPP Password Authentication Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef PAP_H
+#define PAP_H
+
+#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ const char *us_user; /* User */
+ int us_userlen; /* User length */
+ const char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+extern upap_state upap[];
+
+void upap_authwithpeer (int, char *, char *);
+void upap_authpeer (int);
+
+extern struct protent pap_protent;
+
+#endif /* PAP_SUPPORT */
+
+#endif /* PAP_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/ppp.c b/Libraries/Lwip/lwip/src/netif/ppp/ppp.c
new file mode 100644
index 00000000..8e8fae9f
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/ppp.c
@@ -0,0 +1,2045 @@
+/*****************************************************************************
+* ppp.c - Network Point to Point Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * if_pppvar.h - private structures and declarations for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "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 <string.h>
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback().
+ * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1.
+ * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded).
+ */
+#ifndef PPP_INPROC_MULTITHREADED
+#define PPP_INPROC_MULTITHREADED (NO_SYS==0)
+#endif
+
+/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session.
+ * Default is 0: call pppos_input() for received raw characters, charcater
+ * reception is up to the port */
+#ifndef PPP_INPROC_OWNTHREAD
+#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED
+#endif
+
+#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED
+ #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1"
+#endif
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_ADDRESS(p) (((u_char *)(p))[0])
+#define PPP_CONTROL(p) (((u_char *)(p))[1])
+#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/* PPP packet parser states. Current state indicates operation yet to be
+ * completed. */
+typedef enum {
+ PDIDLE = 0, /* Idle state - waiting. */
+ PDSTART, /* Process start flag. */
+ PDADDRESS, /* Process address field. */
+ PDCONTROL, /* Process control field. */
+ PDPROTOCOL1, /* Process protocol field 1. */
+ PDPROTOCOL2, /* Process protocol field 2. */
+ PDDATA /* Process data byte. */
+} PPPDevStates;
+
+#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07])
+
+/************************/
+/*** LOCAL DATA TYPES ***/
+/************************/
+
+/** RX buffer size: this may be configured smaller! */
+#ifndef PPPOS_RX_BUFSIZE
+#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN)
+#endif
+
+typedef struct PPPControlRx_s {
+ /** unit number / ppp descriptor */
+ int pd;
+ /** the rx file descriptor */
+ sio_fd_t fd;
+ /** receive buffer - encoded data is stored here */
+#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 ***/
+/******************************/
+u_long subnetMask;
+
+static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *ppp_protocols[] = {
+ &lcp_protent,
+#if PAP_SUPPORT
+ &pap_protent,
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ &chap_protent,
+#endif /* CHAP_SUPPORT */
+#if CBCP_SUPPORT
+ &cbcp_protent,
+#endif /* CBCP_SUPPORT */
+ &ipcp_protent,
+#if CCP_SUPPORT
+ &ccp_protent,
+#endif /* CCP_SUPPORT */
+ NULL
+};
+
+
+/*
+ * Buffers for outgoing packets. This must be accessed only from the appropriate
+ * PPP task so that it doesn't need to be protected to avoid collisions.
+ */
+u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
+
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+
+#if PPPOS_SUPPORT
+/*
+ * FCS lookup table as calculated by genfcstab.
+ * @todo: smaller, slower implementation for lower memory footprint?
+ */
+static const u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/* PPP's Asynchronous-Control-Character-Map. The mask array is used
+ * to select the specific bit for a character. */
+static u_char pppACCMMask[] = {
+ 0x01,
+ 0x02,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80
+};
+
+#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();
+
+ subnetMask = PP_HTONL(0xffffff00UL);
+
+ for (i = 0; i < NUM_PPP; i++) {
+ /* Initialize each protocol to the standard option set. */
+ for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) {
+ (*protp->init)(i);
+ }
+ }
+}
+
+void
+pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd)
+{
+ switch(authType) {
+ case PPPAUTHTYPE_NONE:
+ default:
+#ifdef LWIP_PPP_STRICT_PAP_REJECT
+ ppp_settings.refuse_pap = 1;
+#else /* LWIP_PPP_STRICT_PAP_REJECT */
+ /* some providers request pap and accept an empty login/pw */
+ ppp_settings.refuse_pap = 0;
+#endif /* LWIP_PPP_STRICT_PAP_REJECT */
+ ppp_settings.refuse_chap = 1;
+ break;
+
+ case PPPAUTHTYPE_ANY:
+ /* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
+ * RFC 1994 says:
+ *
+ * In practice, within or associated with each PPP server, there is a
+ * database which associates "user" names with authentication
+ * information ("secrets"). It is not anticipated that a particular
+ * named user would be authenticated by multiple methods. This would
+ * make the user vulnerable to attacks which negotiate the least secure
+ * method from among a set (such as PAP rather than CHAP). If the same
+ * secret was used, PAP would reveal the secret to be used later with
+ * CHAP.
+ *
+ * Instead, for each user name there should be an indication of exactly
+ * one method used to authenticate that user name. If a user needs to
+ * make use of different authentication methods under different
+ * circumstances, then distinct user names SHOULD be employed, each of
+ * which identifies exactly one authentication method.
+ *
+ */
+ ppp_settings.refuse_pap = 0;
+ ppp_settings.refuse_chap = 0;
+ break;
+
+ case PPPAUTHTYPE_PAP:
+ ppp_settings.refuse_pap = 0;
+ ppp_settings.refuse_chap = 1;
+ break;
+
+ case PPPAUTHTYPE_CHAP:
+ ppp_settings.refuse_pap = 1;
+ ppp_settings.refuse_chap = 0;
+ break;
+ }
+
+ if(user) {
+ strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1);
+ ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0';
+ } else {
+ ppp_settings.user[0] = '\0';
+ }
+
+ if(passwd) {
+ strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1);
+ ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0';
+ } else {
+ ppp_settings.passwd[0] = '\0';
+ }
+}
+
+#if PPPOS_SUPPORT
+/** Open a new PPP connection using the given I/O device.
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session. If this port
+ * connects to a modem, the modem connection must be
+ * established before calling this.
+ * Return a new PPP connection descriptor on success or
+ * an error code (negative) on failure.
+ *
+ * pppOpen() is directly defined to this function.
+ */
+int
+pppOverSerialOpen(sio_fd_t fd, 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 = subnetMask | htonl(nmask);
+
+ /* XXX
+ * Scan through the system's network interfaces.
+ * Get each netmask and OR them into our mask.
+ */
+
+ return mask;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid)
+{
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPControl *pc = &pppControl[pd];
+
+ pc->vjEnabled = vjcomp;
+ pc->vjComp.compressSlot = cidcomp;
+ pc->vjComp.maxSlotIndex = maxcid;
+ PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n",
+ vjcomp, cidcomp, maxcid));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ LWIP_UNUSED_ARG(pd);
+ LWIP_UNUSED_ARG(vjcomp);
+ LWIP_UNUSED_ARG(cidcomp);
+ LWIP_UNUSED_ARG(maxcid);
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+
+ return 0;
+}
+
+/*
+ * pppifNetifInit - netif init callback
+ */
+static err_t
+pppifNetifInit(struct netif *netif)
+{
+ netif->name[0] = 'p';
+ netif->name[1] = 'p';
+ netif->output = pppifOutput;
+ netif->mtu = pppMTU((int)(size_t)netif->state);
+ netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP;
+#if LWIP_NETIF_HOSTNAME
+ /* @todo: Initialize interface hostname */
+ /* netif_set_hostname(netif, "lwip"); */
+#endif /* LWIP_NETIF_HOSTNAME */
+ return ERR_OK;
+}
+
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_remove(&pc->netif);
+ if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask,
+ &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) {
+ netif_set_up(&pc->netif);
+ pc->if_up = 1;
+ pc->errCode = PPPERR_NONE;
+
+ PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs);
+ }
+ } else {
+ st = 0;
+ PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd));
+ }
+ }
+
+ return st;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(int u, int proto, enum NPmode mode)
+{
+ LWIP_UNUSED_ARG(u);
+ LWIP_UNUSED_ARG(proto);
+ LWIP_UNUSED_ARG(mode);
+ return 0;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd));
+ } else {
+ pc->if_up = 0;
+ /* make sure the netif status callback is called */
+ netif_set_down(&pc->netif);
+ netif_remove(&pc->netif);
+ PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL);
+ }
+ }
+ return st;
+}
+
+/**
+ * sifaddr - Config the interface IP addresses and netmask.
+ * @param pd Interface unit ???
+ * @param o Our IP address ???
+ * @param h His IP address ???
+ * @param m IP subnet mask ???
+ * @param ns1 Primary DNS
+ * @param ns2 Secondary DNS
+ */
+int
+sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o));
+ SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h));
+ SMEMCPY(&pc->addrs.netmask, &m, sizeof(m));
+ SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1));
+ SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2));
+ }
+ return st;
+}
+
+/**
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ * @param pd Interface unit ???
+ * @param o Our IP address ???
+ * @param h IP broadcast address ???
+ */
+int
+cifaddr( int pd, u32_t o, u32_t h)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(o);
+ LWIP_UNUSED_ARG(h);
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.netmask, 255,255,255,0);
+ IP4_ADDR(&pc->addrs.dns1, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.dns2, 0,0,0,0);
+ }
+ return st;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(int pd, u32_t l, u32_t g)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(l);
+ LWIP_UNUSED_ARG(g);
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_set_default(&pc->netif);
+ }
+
+ /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */
+
+ return st;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(int pd, u32_t l, u32_t g)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(l);
+ LWIP_UNUSED_ARG(g);
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_set_default(NULL);
+ }
+
+ return st;
+}
+
+/**********************************/
+/*** LOCAL FUNCTION DEFINITIONS ***/
+/**********************************/
+
+#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD
+/* The main PPP process function. This implements the state machine according
+ * to section 4 of RFC 1661: The Point-To-Point Protocol. */
+static void
+pppInputThread(void *arg)
+{
+ int count;
+ PPPControlRx *pcrx = arg;
+
+ while (lcp_phase[pcrx->pd] != PHASE_DEAD) {
+ count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE);
+ if(count > 0) {
+ pppInProc(pcrx, pcrx->rxbuf, count);
+ } else {
+ /* nothing received, give other tasks a chance to run */
+ sys_msleep(1);
+ }
+ }
+}
+#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */
+
+#if PPPOE_SUPPORT
+
+void
+pppOverEthernetInitFailed(int pd)
+{
+ PPPControl* pc;
+
+ pppHup(pd);
+ pppStop(pd);
+
+ pc = &pppControl[pd];
+ pppoe_destroy(&pc->netif);
+ pc->openFlag = 0;
+
+ if(pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
+ }
+}
+
+static void
+pppOverEthernetLinkStatusCB(int pd, int up)
+{
+ if(up) {
+ PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd));
+ pppStart(pd);
+ } else {
+ pppOverEthernetInitFailed(pd);
+ }
+}
+#endif /* PPPOE_SUPPORT */
+
+struct pbuf *
+pppSingleBuf(struct pbuf *p)
+{
+ struct pbuf *q, *b;
+ u_char *pl;
+
+ if(p->tot_len == p->len) {
+ return p;
+ }
+
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(!q) {
+ PPPDEBUG(LOG_ERR,
+ ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len));
+ return p; /* live dangerously */
+ }
+
+ for(b = p, pl = q->payload; b != NULL; b = b->next) {
+ MEMCPY(pl, b->payload, b->len);
+ pl += b->len;
+ }
+
+ pbuf_free(p);
+
+ return q;
+}
+
+struct pppInputHeader {
+ int unit;
+ u16_t proto;
+};
+
+/*
+ * Pass the processed input packet to the appropriate handler.
+ * This function and all handlers run in the context of the tcpip_thread
+ */
+static void
+pppInput(void *arg)
+{
+ struct pbuf *nb = (struct pbuf *)arg;
+ u16_t protocol;
+ int pd;
+
+ pd = ((struct pppInputHeader *)nb->payload)->unit;
+ protocol = ((struct pppInputHeader *)nb->payload)->proto;
+
+ if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+
+ LINK_STATS_INC(link.recv);
+ snmp_inc_ifinucastpkts(&pppControl[pd].netif);
+ snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len);
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) {
+ if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) ||
+ (lcp_phase[pd] != PHASE_AUTHENTICATE)) {
+ PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd]));
+ goto drop;
+ }
+ }
+
+ switch(protocol) {
+ case PPP_VJC_COMP: /* VJ compressed TCP */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len));
+ /*
+ * Clip off the VJ header and prepend the rebuilt TCP/IP header and
+ * pass the result to IP.
+ */
+ if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ /* No handler for this protocol so drop the packet. */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload));
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+ break;
+
+ case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len));
+ /*
+ * Process the TCP/IP header for VJ header compression and then pass
+ * the packet to IP.
+ */
+ if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ /* No handler for this protocol so drop the packet. */
+ PPPDEBUG(LOG_INFO,
+ ("pppInput[%d]: drop VJ UnComp in %d:.*H\n",
+ pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload));
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+ break;
+
+ case PPP_IP: /* Internet Protocol */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len));
+ if (pppControl[pd].netif.input) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ break;
+
+ default: {
+ struct protent *protp;
+ int i;
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len));
+ nb = pppSingleBuf(nb);
+ (*protp->input)(pd, nb->payload, nb->len);
+ PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd));
+ goto out;
+ }
+ }
+
+ /* No handler for this protocol so reject the packet. */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len));
+ if (pbuf_header(nb, sizeof(protocol))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+#if BYTE_ORDER == LITTLE_ENDIAN
+ protocol = htons(protocol);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ SMEMCPY(nb->payload, &protocol, sizeof(protocol));
+ lcp_sprotrej(pd, nb->payload, nb->len);
+ }
+ break;
+ }
+
+drop:
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pd].netif);
+
+out:
+ pbuf_free(nb);
+ return;
+}
+
+#if PPPOS_SUPPORT
+/*
+ * Drop the input packet.
+ */
+static void
+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/Libraries/Lwip/lwip/src/netif/ppp/ppp.h b/Libraries/Lwip/lwip/src/netif/ppp/ppp.h
new file mode 100644
index 00000000..08d6e62d
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/ppp.h
@@ -0,0 +1,201 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+
+#ifndef PPP_H
+#define PPP_H
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/sio.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+
+
+#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/Libraries/Lwip/lwip/src/netif/ppp/ppp_impl.h b/Libraries/Lwip/lwip/src/netif/ppp/ppp_impl.h
new file mode 100644
index 00000000..89aea60b
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/ppp_impl.h
@@ -0,0 +1,363 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+
+#ifndef PPP_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/Libraries/Lwip/lwip/src/netif/ppp/ppp_oe.c b/Libraries/Lwip/lwip/src/netif/ppp/ppp_oe.c
new file mode 100644
index 00000000..fdf52ae2
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/ppp_oe.c
@@ -0,0 +1,1132 @@
+/*****************************************************************************
+* ppp_oe.c - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp_oe.h"
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "lwip/timers.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+#include <stdio.h>
+
+
+/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
+#define PPPOE_ADD_16(PTR, VAL) \
+ *(PTR)++ = (u8_t)((VAL) / 256); \
+ *(PTR)++ = (u8_t)((VAL) % 256)
+
+/* Add a complete PPPoE header to the buffer pointed to by PTR */
+#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \
+ *(PTR)++ = PPPOE_VERTYPE; \
+ *(PTR)++ = (CODE); \
+ PPPOE_ADD_16(PTR, SESS); \
+ PPPOE_ADD_16(PTR, LEN)
+
+#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */
+#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */
+#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */
+#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */
+
+#ifdef PPPOE_SERVER
+#error "PPPOE_SERVER is not yet supported under lwIP!"
+/* from if_spppsubr.c */
+#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */
+#endif
+
+#ifndef PPPOE_ERRORSTRING_LEN
+#define PPPOE_ERRORSTRING_LEN 64
+#endif
+static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN];
+
+
+/* input routines */
+static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *);
+
+/* management routines */
+static int pppoe_do_disconnect(struct pppoe_softc *);
+static void pppoe_abort_connect(struct pppoe_softc *);
+static void pppoe_clear_softc(struct pppoe_softc *, const char *);
+
+/* internal timeout handling */
+static void pppoe_timeout(void *);
+
+/* sending actual protocol controll packets */
+static err_t pppoe_send_padi(struct pppoe_softc *);
+static err_t pppoe_send_padr(struct pppoe_softc *);
+#ifdef PPPOE_SERVER
+static err_t pppoe_send_pado(struct pppoe_softc *);
+static err_t pppoe_send_pads(struct pppoe_softc *);
+#endif
+static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *);
+
+/* internal helper functions */
+static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *);
+static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *);
+
+/** linked list of created pppoe interfaces */
+static struct pppoe_softc *pppoe_softc_list;
+
+err_t
+pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr)
+{
+ struct pppoe_softc *sc;
+
+ sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF);
+ if (sc == NULL) {
+ *scptr = NULL;
+ return ERR_MEM;
+ }
+ memset(sc, 0, sizeof(struct pppoe_softc));
+
+ /* changed to real address later */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+
+ sc->sc_pd = pd;
+ sc->sc_linkStatusCB = linkStatusCB;
+ sc->sc_ethif = ethif;
+
+ /* put the new interface at the head of the list */
+ sc->next = pppoe_softc_list;
+ pppoe_softc_list = sc;
+
+ *scptr = sc;
+
+ return ERR_OK;
+}
+
+err_t
+pppoe_destroy(struct netif *ifp)
+{
+ struct pppoe_softc *sc, *prev = NULL;
+
+ for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) {
+ if (sc->sc_ethif == ifp) {
+ break;
+ }
+ }
+
+ if(!(sc && (sc->sc_ethif == ifp))) {
+ return ERR_IF;
+ }
+
+ sys_untimeout(pppoe_timeout, sc);
+ if (prev == NULL) {
+ /* remove sc from the head of the list */
+ pppoe_softc_list = sc->next;
+ } else {
+ /* remove sc from the list */
+ prev->next = sc->next;
+ }
+
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name) {
+ mem_free(sc->sc_concentrator_name);
+ }
+ if (sc->sc_service_name) {
+ mem_free(sc->sc_service_name);
+ }
+#endif /* PPPOE_TODO */
+ memp_free(MEMP_PPPOE_IF, sc);
+
+ return ERR_OK;
+}
+
+/*
+ * Find the interface handling the specified session.
+ * Note: O(number of sessions open), this is a client-side only, mean
+ * and lean implementation, so number of open sessions typically should
+ * be 1.
+ */
+static struct pppoe_softc *
+pppoe_find_softc_by_session(u_int session, struct netif *rcvif)
+{
+ struct pppoe_softc *sc;
+
+ if (session == 0) {
+ return NULL;
+ }
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc->sc_state == PPPOE_STATE_SESSION
+ && sc->sc_session == session) {
+ if (sc->sc_ethif == rcvif) {
+ return sc;
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Check host unique token passed and return appropriate softc pointer,
+ * or NULL if token is bogus. */
+static struct pppoe_softc *
+pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif)
+{
+ struct pppoe_softc *sc, *t;
+
+ if (pppoe_softc_list == NULL) {
+ return NULL;
+ }
+
+ if (len != sizeof sc) {
+ return NULL;
+ }
+ MEMCPY(&t, token, len);
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc == t) {
+ break;
+ }
+ }
+
+ if (sc == NULL) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n"));
+ return NULL;
+ }
+
+ /* should be safe to access *sc now */
+ if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
+ printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state);
+ return NULL;
+ }
+ if (sc->sc_ethif != rcvif) {
+ printf("%c%c%"U16_F": wrong interface, not accepting host unique\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ return NULL;
+ }
+ return sc;
+}
+
+static void
+pppoe_linkstatus_up(struct pppoe_softc *sc)
+{
+ sc->sc_linkStatusCB(sc->sc_pd, 1);
+}
+
+/* analyze and handle a single received packet while not in session state */
+static void
+pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
+{
+ u16_t tag, len;
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+ const char *err_msg;
+ char devname[6];
+ u8_t *ac_cookie;
+ u16_t ac_cookie_len;
+#ifdef PPPOE_SERVER
+ u8_t *hunique;
+ size_t hunique_len;
+#endif
+ struct pppoehdr *ph;
+ struct pppoetag pt;
+ int off, err, errortag;
+ struct eth_hdr *ethhdr;
+
+ pb = pppSingleBuf(pb);
+
+ strcpy(devname, "pppoe"); /* as long as we don't know which instance */
+ err_msg = NULL;
+ errortag = 0;
+ if (pb->len < sizeof(*ethhdr)) {
+ goto done;
+ }
+ ethhdr = (struct eth_hdr *)pb->payload;
+ off = sizeof(*ethhdr);
+
+ ac_cookie = NULL;
+ ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ hunique = NULL;
+ hunique_len = 0;
+#endif
+ session = 0;
+ if (pb->len - off < PPPOE_HEADERLEN) {
+ printf("pppoe: packet too short: %d\n", pb->len);
+ goto done;
+ }
+
+ ph = (struct pppoehdr *) (ethhdr + 1);
+ if (ph->vertype != PPPOE_VERTYPE) {
+ printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype);
+ goto done;
+ }
+ session = ntohs(ph->session);
+ plen = ntohs(ph->plen);
+ off += sizeof(*ph);
+
+ if (plen + off > pb->len) {
+ printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
+ pb->len - off, plen);
+ goto done;
+ }
+ if(pb->tot_len == pb->len) {
+ pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */
+ }
+ tag = 0;
+ len = 0;
+ sc = NULL;
+ while (off + sizeof(pt) <= pb->len) {
+ MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt));
+ tag = ntohs(pt.tag);
+ len = ntohs(pt.len);
+ if (off + sizeof(pt) + len > pb->len) {
+ printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len);
+ goto done;
+ }
+ switch (tag) {
+ case PPPOE_TAG_EOL:
+ goto breakbreak;
+ case PPPOE_TAG_SNAME:
+ break; /* ignored */
+ case PPPOE_TAG_ACNAME:
+ break; /* ignored */
+ case PPPOE_TAG_HUNIQUE:
+ if (sc != NULL) {
+ break;
+ }
+#ifdef PPPOE_SERVER
+ hunique = (u8_t*)pb->payload + off + sizeof(pt);
+ hunique_len = len;
+#endif
+ sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif);
+ if (sc != NULL) {
+ snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ }
+ break;
+ case PPPOE_TAG_ACCOOKIE:
+ if (ac_cookie == NULL) {
+ ac_cookie = (u8_t*)pb->payload + off + sizeof(pt);
+ ac_cookie_len = len;
+ }
+ break;
+ case PPPOE_TAG_SNAME_ERR:
+ err_msg = "SERVICE NAME ERROR";
+ errortag = 1;
+ break;
+ case PPPOE_TAG_ACSYS_ERR:
+ err_msg = "AC SYSTEM ERROR";
+ errortag = 1;
+ break;
+ case PPPOE_TAG_GENERIC_ERR:
+ err_msg = "GENERIC ERROR";
+ errortag = 1;
+ break;
+ }
+ if (err_msg) {
+ if (errortag && len) {
+ u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1);
+ strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
+ pppoe_error_tmp[error_len-1] = '\0';
+ printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp);
+ } else {
+ printf("%s: %s\n", devname, err_msg);
+ }
+ if (errortag) {
+ goto done;
+ }
+ }
+ off += sizeof(pt) + len;
+ }
+
+breakbreak:;
+ switch (ph->code) {
+ case PPPOE_CODE_PADI:
+#ifdef PPPOE_SERVER
+ /*
+ * got service name, concentrator name, and/or host unique.
+ * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP.
+ */
+ if (LIST_EMPTY(&pppoe_softc_list)) {
+ goto done;
+ }
+ LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+ if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) {
+ continue;
+ }
+ if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ continue;
+ }
+ if (sc->sc_state == PPPOE_STATE_INITIAL) {
+ break;
+ }
+ }
+ if (sc == NULL) {
+ /* printf("pppoe: free passive interface is not found\n"); */
+ goto done;
+ }
+ if (hunique) {
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ }
+ sc->sc_hunique = mem_malloc(hunique_len);
+ if (sc->sc_hunique == NULL) {
+ goto done;
+ }
+ sc->sc_hunique_len = hunique_len;
+ MEMCPY(sc->sc_hunique, hunique, hunique_len);
+ }
+ MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest);
+ sc->sc_state = PPPOE_STATE_PADO_SENT;
+ pppoe_send_pado(sc);
+ break;
+#endif /* PPPOE_SERVER */
+ case PPPOE_CODE_PADR:
+#ifdef PPPOE_SERVER
+ /*
+ * get sc from ac_cookie if IFF_PASSIVE
+ */
+ if (ac_cookie == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ printf("pppoe: received PADR but not includes ac_cookie\n");
+ goto done;
+ }
+ sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif);
+ if (sc == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ if (!LIST_EMPTY(&pppoe_softc_list)) {
+ printf("pppoe: received PADR but could not find request for it\n");
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ goto done;
+ }
+ if (hunique) {
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ }
+ sc->sc_hunique = mem_malloc(hunique_len);
+ if (sc->sc_hunique == NULL) {
+ goto done;
+ }
+ sc->sc_hunique_len = hunique_len;
+ MEMCPY(sc->sc_hunique, hunique, hunique_len);
+ }
+ pppoe_send_pads(sc);
+ sc->sc_state = PPPOE_STATE_SESSION;
+ pppoe_linkstatus_up(sc); /* notify upper layers */
+ break;
+#else
+ /* ignore, we are no access concentrator */
+ goto done;
+#endif /* PPPOE_SERVER */
+ case PPPOE_CODE_PADO:
+ if (sc == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ if (pppoe_softc_list != NULL) {
+ printf("pppoe: received PADO but could not find request for it\n");
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
+ printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ goto done;
+ }
+ if (ac_cookie) {
+ sc->sc_ac_cookie_len = ac_cookie_len;
+ MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len);
+ }
+ MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr));
+ sys_untimeout(pppoe_timeout, sc);
+ sc->sc_padr_retried = 0;
+ sc->sc_state = PPPOE_STATE_PADR_SENT;
+ if ((err = pppoe_send_padr(sc)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+ break;
+ case PPPOE_CODE_PADS:
+ if (sc == NULL) {
+ goto done;
+ }
+ sc->sc_session = session;
+ sys_untimeout(pppoe_timeout, sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session));
+ sc->sc_state = PPPOE_STATE_SESSION;
+ pppoe_linkstatus_up(sc); /* notify upper layers */
+ break;
+ case PPPOE_CODE_PADT:
+ if (sc == NULL) {
+ goto done;
+ }
+ pppoe_clear_softc(sc, "received PADT");
+ break;
+ default:
+ if(sc) {
+ printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+ (u16_t)ph->code, session);
+ } else {
+ printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session);
+ }
+ break;
+ }
+
+done:
+ pbuf_free(pb);
+ return;
+}
+
+void
+pppoe_disc_input(struct netif *netif, struct pbuf *p)
+{
+ /* avoid error messages if there is not a single pppoe instance */
+ if (pppoe_softc_list != NULL) {
+ pppoe_dispatch_disc_pkt(netif, p);
+ } else {
+ pbuf_free(p);
+ }
+}
+
+void
+pppoe_data_input(struct netif *netif, struct pbuf *pb)
+{
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+ struct pppoehdr *ph;
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ u8_t shost[ETHER_ADDR_LEN];
+#endif
+
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost));
+#endif
+ if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ pb = pppSingleBuf (pb);
+
+ if (pb->len <= PPPOE_HEADERLEN) {
+ printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len);
+ goto drop;
+ }
+
+ if (pb->len < sizeof(*ph)) {
+ printf("pppoe_data_input: could not get PPPoE header\n");
+ goto drop;
+ }
+ ph = (struct pppoehdr *)pb->payload;
+
+ if (ph->vertype != PPPOE_VERTYPE) {
+ printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype);
+ goto drop;
+ }
+ if (ph->code != 0) {
+ goto drop;
+ }
+
+ session = ntohs(ph->session);
+ sc = pppoe_find_softc_by_session(session, netif);
+ if (sc == NULL) {
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ printf("pppoe: input for unknown session 0x%x, sending PADT\n", session);
+ pppoe_send_padt(netif, session, shost);
+#endif
+ goto drop;
+ }
+
+ plen = ntohs(ph->plen);
+
+ if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+ pb->len, plen));
+
+ if (pb->len < plen) {
+ goto drop;
+ }
+
+ pppInProcOverEthernet(sc->sc_pd, pb);
+
+ return;
+
+drop:
+ pbuf_free(pb);
+}
+
+static err_t
+pppoe_output(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ struct eth_hdr *ethhdr;
+ u16_t etype;
+ err_t res;
+
+ if (!sc->sc_ethif) {
+ pbuf_free(pb);
+ return ERR_IF;
+ }
+
+ ethhdr = (struct eth_hdr *)pb->payload;
+ etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
+ ethhdr->type = htons(etype);
+ MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+ MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr));
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype,
+ sc->sc_state, sc->sc_session,
+ sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5],
+ pb->tot_len));
+
+ res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb);
+
+ pbuf_free(pb);
+
+ return res;
+}
+
+static err_t
+pppoe_send_padi(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ int len;
+#ifdef PPPOE_TODO
+ int l1 = 0, l2 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ if (sc->sc_state >PPPOE_STATE_PADI_SENT) {
+ PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state));
+ }
+
+ /* calculate length of frame (excluding ethernet header + pppoe header) */
+ len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ l1 = (int)strlen(sc->sc_service_name);
+ len += l1;
+ }
+ if (sc->sc_concentrator_name != NULL) {
+ l2 = (int)strlen(sc->sc_concentrator_name);
+ len += 2 + 2 + l2;
+ }
+#endif /* PPPOE_TODO */
+ LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+ sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ /* fill in pkt */
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name != NULL) {
+ PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
+ PPPOE_ADD_16(p, l2);
+ MEMCPY(p, sc->sc_concentrator_name, l2);
+ p += l2;
+ }
+#endif /* PPPOE_TODO */
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof sc);
+
+ /* send pkt */
+ return pppoe_output(sc, pb);
+}
+
+static void
+pppoe_timeout(void *arg)
+{
+ int retry_wait, err;
+ struct pppoe_softc *sc = (struct pppoe_softc*)arg;
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+
+ switch (sc->sc_state) {
+ case PPPOE_STATE_PADI_SENT:
+ /*
+ * We have two basic ways of retrying:
+ * - Quick retry mode: try a few times in short sequence
+ * - Slow retry mode: we already had a connection successfully
+ * established and will try infinitely (without user
+ * intervention)
+ * We only enter slow retry mode if IFF_LINK1 (aka autodial)
+ * is not set.
+ */
+
+ /* initialize for quick retry mode */
+ retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried);
+
+ sc->sc_padi_retried++;
+ if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
+#if 0
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) {
+ /* slow retry mode */
+ retry_wait = PPPOE_SLOW_RETRY;
+ } else
+#endif
+ {
+ pppoe_abort_connect(sc);
+ return;
+ }
+ }
+ if ((err = pppoe_send_padi(sc)) != 0) {
+ sc->sc_padi_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(retry_wait, pppoe_timeout, sc);
+ break;
+
+ case PPPOE_STATE_PADR_SENT:
+ sc->sc_padr_retried++;
+ if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) {
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ sc->sc_padr_retried = 0;
+ if ((err = pppoe_send_padi(sc)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc);
+ return;
+ }
+ if ((err = pppoe_send_padr(sc)) != 0) {
+ sc->sc_padr_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+ break;
+ case PPPOE_STATE_CLOSING:
+ pppoe_do_disconnect(sc);
+ break;
+ default:
+ return; /* all done, work in peace */
+ }
+}
+
+/* Start a connection (i.e. initiate discovery phase) */
+int
+pppoe_connect(struct pppoe_softc *sc)
+{
+ int err;
+
+ if (sc->sc_state != PPPOE_STATE_INITIAL) {
+ return EBUSY;
+ }
+
+#ifdef PPPOE_SERVER
+ /* wait PADI if IFF_PASSIVE */
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ return 0;
+ }
+#endif
+ /* save state, in case we fail to send PADI */
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ sc->sc_padr_retried = 0;
+ err = pppoe_send_padi(sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc);
+ return err;
+}
+
+/* disconnect */
+void
+pppoe_disconnect(struct pppoe_softc *sc)
+{
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ return;
+ }
+ /*
+ * Do not call pppoe_disconnect here, the upper layer state
+ * machine gets confused by this. We must return from this
+ * function and defer disconnecting to the timeout handler.
+ */
+ sc->sc_state = PPPOE_STATE_CLOSING;
+ sys_timeout(20, pppoe_timeout, sc);
+}
+
+static int
+pppoe_do_disconnect(struct pppoe_softc *sc)
+{
+ int err;
+
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ err = EBUSY;
+ } else {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
+ }
+
+ /* cleanup softc */
+ sc->sc_state = PPPOE_STATE_INITIAL;
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ sc->sc_hunique = NULL;
+ }
+ sc->sc_hunique_len = 0;
+#endif
+ sc->sc_session = 0;
+
+ sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
+
+ return err;
+}
+
+/* Connection attempt aborted */
+static void
+pppoe_abort_connect(struct pppoe_softc *sc)
+{
+ printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ sc->sc_state = PPPOE_STATE_CLOSING;
+
+ sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
+
+ /* clear connection state */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_state = PPPOE_STATE_INITIAL;
+}
+
+/* Send a PADR packet */
+static err_t
+pppoe_send_padr(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len;
+#ifdef PPPOE_TODO
+ size_t l1 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ if (sc->sc_state != PPPOE_STATE_PADR_SENT) {
+ return ERR_CONN;
+ }
+
+ len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+#endif /* PPPOE_TODO */
+ if (sc->sc_ac_cookie_len > 0) {
+ len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */
+ }
+ LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+ sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+ if (sc->sc_ac_cookie_len > 0) {
+ PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+ PPPOE_ADD_16(p, sc->sc_ac_cookie_len);
+ MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len);
+ p += sc->sc_ac_cookie_len;
+ }
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof sc);
+
+ return pppoe_output(sc, pb);
+}
+
+/* send a PADT packet */
+static err_t
+pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest)
+{
+ struct pbuf *pb;
+ struct eth_hdr *ethhdr;
+ err_t res;
+ u8_t *p;
+
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ ethhdr = (struct eth_hdr *)pb->payload;
+ ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC);
+ MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
+ MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr));
+
+ p = (u8_t*)(ethhdr + 1);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0);
+
+ res = outgoing_if->linkoutput(outgoing_if, pb);
+
+ pbuf_free(pb);
+
+ return res;
+}
+
+#ifdef PPPOE_SERVER
+static err_t
+pppoe_send_pado(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len;
+
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ return ERR_CONN;
+ }
+
+ /* calc length */
+ len = 0;
+ /* include ac_cookie */
+ len += 2 + 2 + sizeof(sc);
+ /* include hunique */
+ len += 2 + 2 + sc->sc_hunique_len;
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof(sc));
+ p += sizeof(sc);
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sc->sc_hunique_len);
+ MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+ return pppoe_output(sc, pb);
+}
+
+static err_t
+pppoe_send_pads(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len, l1 = 0; /* XXX: gcc */
+
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ return ERR_CONN;
+ }
+
+ sc->sc_session = mono_time.tv_sec % 0xff + 1;
+ /* calc length */
+ len = 0;
+ /* include hunique */
+ len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else {
+ PPPOE_ADD_16(p, 0);
+ }
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sc->sc_hunique_len);
+ MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+ return pppoe_output(sc, pb);
+}
+#endif
+
+err_t
+pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ u8_t *p;
+ size_t len;
+
+ /* are we ready to process data yet? */
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ /*sppp_flush(&sc->sc_sppp.pp_if);*/
+ pbuf_free(pb);
+ return ERR_CONN;
+ }
+
+ len = pb->tot_len;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ LINK_STATS_INC(link.lenerr);
+ pbuf_free(pb);
+ return ERR_BUF;
+ }
+
+ p = (u8_t*)pb->payload + sizeof(struct eth_hdr);
+ PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
+
+ return pppoe_output(sc, pb);
+}
+
+#if 0 /*def PFIL_HOOKS*/
+static int
+pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
+{
+ struct pppoe_softc *sc;
+ int s;
+
+ if (mp != (struct pbuf **)PFIL_IFNET_DETACH) {
+ return 0;
+ }
+
+ LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+ if (sc->sc_ethif != ifp) {
+ continue;
+ }
+ if (sc->sc_sppp.pp_if.if_flags & IFF_UP) {
+ sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
+ printf("%c%c%"U16_F": ethernet interface detached, going down\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ }
+ sc->sc_ethif = NULL;
+ pppoe_clear_softc(sc, "ethernet interface detached");
+ }
+
+ return 0;
+}
+#endif
+
+static void
+pppoe_clear_softc(struct pppoe_softc *sc, const char *message)
+{
+ LWIP_UNUSED_ARG(message);
+
+ /* stop timer */
+ sys_untimeout(pppoe_timeout, sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message));
+
+ /* fix our state */
+ sc->sc_state = PPPOE_STATE_INITIAL;
+
+ /* notify upper layers */
+ sc->sc_linkStatusCB(sc->sc_pd, 0);
+
+ /* clean up softc */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_ac_cookie_len = 0;
+ sc->sc_session = 0;
+}
+
+#endif /* PPPOE_SUPPORT */
+
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/pppdebug.h b/Libraries/Lwip/lwip/src/netif/ppp/pppdebug.h
new file mode 100644
index 00000000..81349971
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/pppdebug.h
@@ -0,0 +1,73 @@
+/*****************************************************************************
+* pppdebug.h - System debugging utilities.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+* portions Copyright (c) 2001 by Cognizant Pty Ltd.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY (please don't use tabs!)
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-07-29 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*
+*****************************************************************************
+*/
+#ifndef PPPDEBUG_H
+#define PPPDEBUG_H
+
+/* Trace levels. */
+#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_INFO (PPP_DEBUG)
+#define LOG_DETAIL (PPP_DEBUG)
+#define LOG_DEBUG (PPP_DEBUG)
+
+
+#define TRACELCP PPP_DEBUG
+
+#if PPP_DEBUG
+
+#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b)
+
+#else /* PPP_DEBUG */
+
+#define AUTHDEBUG(a, b)
+#define IPCPDEBUG(a, b)
+#define UPAPDEBUG(a, b)
+#define LCPDEBUG(a, b)
+#define FSMDEBUG(a, b)
+#define CHAPDEBUG(a, b)
+#define PPPDEBUG(a, b)
+
+#endif /* PPP_DEBUG */
+
+#endif /* PPPDEBUG_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/randm.c b/Libraries/Lwip/lwip/src/netif/ppp/randm.c
new file mode 100644
index 00000000..b736091f
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/randm.c
@@ -0,0 +1,249 @@
+/*****************************************************************************
+* randm.c - Random number generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-06-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Extracted from avos.
+*****************************************************************************/
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "md5.h"
+#include "randm.h"
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include <string.h>
+
+#if MD5_SUPPORT /* this module depends on MD5 */
+#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+static char randPool[RANDPOOLSZ]; /* Pool of randomness. */
+static long randCount = 0; /* Pseudo-random incrementer */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/*
+ * Initialize the random number generator.
+ *
+ * Since this is to be called on power up, we don't have much
+ * system randomess to work with. Here all we use is the
+ * real-time clock. We'll accumulate more randomness as soon
+ * as things start happening.
+ */
+void
+avRandomInit()
+{
+ avChurnRand(NULL, 0);
+}
+
+/*
+ * Churn the randomness pool on a random event. Call this early and often
+ * on random and semi-random system events to build randomness in time for
+ * usage. For randomly timed events, pass a null pointer and a zero length
+ * and this will use the system timer and other sources to add randomness.
+ * If new random data is available, pass a pointer to that and it will be
+ * included.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ */
+void
+avChurnRand(char *randData, u32_t randLen)
+{
+ MD5_CTX md5;
+
+ /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */
+ MD5Init(&md5);
+ MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
+ if (randData) {
+ MD5Update(&md5, (u_char *)randData, randLen);
+ } else {
+ struct {
+ /* INCLUDE fields for any system sources of randomness */
+ char foobar;
+ } sysData;
+
+ /* Load sysData fields here. */
+ MD5Update(&md5, (u_char *)&sysData, sizeof(sysData));
+ }
+ MD5Final((u_char *)randPool, &md5);
+/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */
+}
+
+/*
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using churnRand().
+ * Note: It's important that there be sufficient randomness in randPool
+ * before this is called for otherwise the range of the result may be
+ * narrow enough to make a search feasible.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ *
+ * XXX Why does he not just call churnRand() for each block? Probably
+ * so that you don't ever publish the seed which could possibly help
+ * predict future values.
+ * XXX Why don't we preserve md5 between blocks and just update it with
+ * randCount each time? Probably there is a weakness but I wish that
+ * it was documented.
+ */
+void
+avGenRand(char *buf, u32_t bufLen)
+{
+ MD5_CTX md5;
+ u_char tmp[16];
+ u32_t n;
+
+ while (bufLen > 0) {
+ n = LWIP_MIN(bufLen, RANDPOOLSZ);
+ MD5Init(&md5);
+ MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
+ MD5Update(&md5, (u_char *)&randCount, sizeof(randCount));
+ MD5Final(tmp, &md5);
+ randCount++;
+ MEMCPY(buf, tmp, n);
+ buf += n;
+ bufLen -= n;
+ }
+}
+
+/*
+ * Return a new random number.
+ */
+u32_t
+avRandom()
+{
+ u32_t newRand;
+
+ avGenRand((char *)&newRand, sizeof(newRand));
+
+ return newRand;
+}
+
+#else /* MD5_SUPPORT */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+static int avRandomized = 0; /* Set when truely randomized. */
+static u32_t avRandomSeed = 0; /* Seed used for random number generation. */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/*
+ * Initialize the random number generator.
+ *
+ * Here we attempt to compute a random number seed but even if
+ * it isn't random, we'll randomize it later.
+ *
+ * The current method uses the fields from the real time clock,
+ * the idle process counter, the millisecond counter, and the
+ * hardware timer tick counter. When this is invoked
+ * in startup(), then the idle counter and timer values may
+ * repeat after each boot and the real time clock may not be
+ * operational. Thus we call it again on the first random
+ * event.
+ */
+void
+avRandomInit()
+{
+#if 0
+ /* Get a pointer into the last 4 bytes of clockBuf. */
+ u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]);
+
+ /*
+ * Initialize our seed using the real-time clock, the idle
+ * counter, the millisecond timer, and the hardware timer
+ * tick counter. The real-time clock and the hardware
+ * tick counter are the best sources of randomness but
+ * since the tick counter is only 16 bit (and truncated
+ * at that), the idle counter and millisecond timer
+ * (which may be small values) are added to help
+ * randomize the lower 16 bits of the seed.
+ */
+ readClk();
+ avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr
+ + ppp_mtime() + ((u32_t)TM1 << 16) + TM1;
+#else
+ avRandomSeed += sys_jiffies(); /* XXX */
+#endif
+
+ /* Initialize the Borland random number generator. */
+ srand((unsigned)avRandomSeed);
+}
+
+/*
+ * Randomize our random seed value. Here we use the fact that
+ * this function is called at *truely random* times by the polling
+ * and network functions. Here we only get 16 bits of new random
+ * value but we use the previous value to randomize the other 16
+ * bits.
+ */
+void
+avRandomize(void)
+{
+ static u32_t last_jiffies;
+
+ if (!avRandomized) {
+ avRandomized = !0;
+ avRandomInit();
+ /* The initialization function also updates the seed. */
+ } else {
+ /* avRandomSeed += (avRandomSeed << 16) + TM1; */
+ avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */
+ }
+ last_jiffies = sys_jiffies();
+}
+
+/*
+ * Return a new random number.
+ * Here we use the Borland rand() function to supply a pseudo random
+ * number which we make truely random by combining it with our own
+ * seed which is randomized by truely random events.
+ * Thus the numbers will be truely random unless there have been no
+ * operator or network events in which case it will be pseudo random
+ * seeded by the real time clock.
+ */
+u32_t
+avRandom()
+{
+ return ((((u32_t)rand() << 16) + rand()) + avRandomSeed);
+}
+
+#endif /* MD5_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/randm.h b/Libraries/Lwip/lwip/src/netif/ppp/randm.h
new file mode 100644
index 00000000..a0984b02
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/randm.h
@@ -0,0 +1,81 @@
+/*****************************************************************************
+* randm.h - Random number generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-05-29 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Extracted from avos.
+*****************************************************************************/
+
+#ifndef RANDM_H
+#define RANDM_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+/*
+ * Initialize the random number generator.
+ */
+void avRandomInit(void);
+
+/*
+ * Churn the randomness pool on a random event. Call this early and often
+ * on random and semi-random system events to build randomness in time for
+ * usage. For randomly timed events, pass a null pointer and a zero length
+ * and this will use the system timer and other sources to add randomness.
+ * If new random data is available, pass a pointer to that and it will be
+ * included.
+ */
+void avChurnRand(char *randData, u32_t randLen);
+
+/*
+ * Randomize our random seed value. To be called for truely random events
+ * such as user operations and network traffic.
+ */
+#if MD5_SUPPORT
+#define avRandomize() avChurnRand(NULL, 0)
+#else /* MD5_SUPPORT */
+void avRandomize(void);
+#endif /* MD5_SUPPORT */
+
+/*
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using churnRand().
+ * Thus it's important to make sure that the results of this are not
+ * published directly because one could predict the next result to at
+ * least some degree. Also, it's important to get a good seed before
+ * the first use.
+ */
+void avGenRand(char *buf, u32_t bufLen);
+
+/*
+ * Return a new random number.
+ */
+u32_t avRandom(void);
+
+
+#endif /* RANDM_H */
diff --git a/Libraries/Lwip/lwip/src/netif/ppp/vj.c b/Libraries/Lwip/lwip/src/netif/ppp/vj.c
new file mode 100644
index 00000000..40fdad13
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/vj.c
@@ -0,0 +1,652 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * Initial distribution.
+ *
+ * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
+ * so that the entire packet being decompressed doesn't have
+ * to be in contiguous memory (just the compressed header).
+ *
+ * Modified March 1998 by Guy Lancaster, glanca@gesn.com,
+ * for a 16 bit processor.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "vj.h"
+
+#include <string.h>
+
+#if VJ_SUPPORT
+
+#if LINK_STATS
+#define INCR(counter) ++comp->stats.counter
+#else
+#define INCR(counter)
+#endif
+
+void
+vj_compress_init(struct vjcompress *comp)
+{
+ register u_char i;
+ register struct cstate *tstate = comp->tstate;
+
+#if MAX_SLOTS == 0
+ memset((char *)comp, 0, sizeof(*comp));
+#endif
+ comp->maxSlotIndex = MAX_SLOTS - 1;
+ comp->compressSlot = 0; /* Disable slot ID compression by default. */
+ for (i = MAX_SLOTS - 1; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = VJF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the long, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (u_char)(n); \
+ cp[0] = (u_char)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u_char)(n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (u_char)(n); \
+ cp[0] = (u_char)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u_char)(n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = htonl(tmp); \
+ cp += 3; \
+ } else { \
+ u32_t tmp = ntohl(f) + (u32_t)*cp++; \
+ (f) = htonl(tmp); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \
+ (f) = htons(tmp); \
+ cp += 3; \
+ } else { \
+ u_short tmp = ntohs(f) + (u_short)*cp++; \
+ (f) = htons(tmp); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons(((u_short)cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_short)*cp++); \
+ } \
+}
+
+/*
+ * vj_compress_tcp - Attempt to do Van Jacobson header compression on a
+ * packet. This assumes that nb and comp are not null and that the first
+ * buffer of the chain contains a valid IP header.
+ * Return the VJ type code indicating whether or not the packet was
+ * compressed.
+ */
+u_int
+vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
+{
+ register struct ip_hdr *ip = (struct ip_hdr *)pb->payload;
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_short hlen = IPH_HL(ip);
+ register struct tcp_hdr *oth;
+ register struct tcp_hdr *th;
+ register u_short deltaS, deltaA;
+ register u_long deltaL;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Check that the packet is IP proto TCP.
+ */
+ if (IPH_PROTO(ip) != IP_PROTO_TCP) {
+ return (TYPE_IP);
+ }
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't
+ * `compressible' (i.e., ACK isn't set or some other control bit is
+ * set).
+ */
+ if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) {
+ return (TYPE_IP);
+ }
+ th = (struct tcp_hdr *)&((long *)ip)[hlen];
+ if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
+ return (TYPE_IP);
+ }
+ /*
+ * Packet is compressible -- we're going to send either a
+ * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
+ * to locate (or create) the connection state. Special case the
+ * most recently used connection since it's most likely to be used
+ * again & we don't have to do any reordering if it's used.
+ */
+ INCR(vjs_packets);
+ if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src)
+ || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with
+ * last_cs pointing to the end of the list. The
+ * list is kept in lru order by moving a state to the
+ * head of the list whenever it is referenced. Since
+ * the list is short and, empirically, the connection
+ * we want is almost always near the front, we locate
+ * states via linear search. If we don't find a state
+ * for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs; cs = cs->cs_next;
+ INCR(vjs_searches);
+ if (ip_addr_cmp(&ip->src, &cs->cs_ip.src)
+ && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ goto found;
+ }
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * last_cs to update the lru linkage.
+ */
+ INCR(vjs_misses);
+ comp->last_cs = lcs;
+ hlen += TCPH_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/Libraries/Lwip/lwip/src/netif/ppp/vj.h b/Libraries/Lwip/lwip/src/netif/ppp/vj.h
new file mode 100644
index 00000000..fad12136
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/ppp/vj.h
@@ -0,0 +1,156 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#ifndef VJ_H
+#define VJ_H
+
+#include "lwip/ip.h"
+#include "lwip/tcp_impl.h"
+
+#define MAX_SLOTS 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used state (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */
+ } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+
+struct vjstat {
+ unsigned long vjs_packets; /* outbound packets */
+ unsigned long vjs_compressed; /* outbound compressed packets */
+ unsigned long vjs_searches; /* searches for connection state */
+ unsigned long vjs_misses; /* times couldn't find conn. state */
+ unsigned long vjs_uncompressedin; /* inbound uncompressed packets */
+ unsigned long vjs_compressedin; /* inbound compressed packets */
+ unsigned long vjs_errorin; /* inbound unknown type packets */
+ unsigned long vjs_tossed; /* inbound packets tossed because of error */
+};
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+ u_char maxSlotIndex;
+ u_char compressSlot; /* Flag indicating OK to compress slot ID. */
+#if LINK_STATS
+ struct vjstat stats;
+#endif
+ struct cstate tstate[MAX_SLOTS]; /* xmit connection states */
+ struct cstate rstate[MAX_SLOTS]; /* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1U /* tossing rcvd frames because of input err */
+
+extern void vj_compress_init (struct vjcompress *comp);
+extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb);
+extern void vj_uncompress_err (struct vjcompress *comp);
+extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp);
+extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp);
+
+#endif /* VJ_H */
diff --git a/Libraries/Lwip/lwip/src/netif/slipif.c b/Libraries/Lwip/lwip/src/netif/slipif.c
new file mode 100644
index 00000000..3c1db193
--- /dev/null
+++ b/Libraries/Lwip/lwip/src/netif/slipif.c
@@ -0,0 +1,510 @@
+/**
+ * @file
+ * SLIP Interface
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is built upon the file: src/arch/rtxc/netif/sioslip.c
+ *
+ * Author: Magnus Ivarsson <magnus.ivarsson(at)volvo.com>
+ * 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 "lwip/src/include/netif/slipif.h"
+#include "lwip/src/include/lwip/opt.h"
+
+#if LWIP_HAVE_SLIPIF
+
+#include "lwip/src/include/lwip/def.h"
+#include "lwip/src/include/lwip/pbuf.h"
+#include "lwip/src/include/lwip/stats.h"
+#include "lwip/src/include/lwip/snmp.h"
+#include "lwip/src/include/lwip/sio.h"
+#include "lwip/src/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
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+err_t
+slipif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ struct slipif_priv *priv;
+ struct pbuf *q;
+ u16_t i;
+ u8_t c;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ LWIP_ASSERT("p != NULL", (p != NULL));
+
+ LWIP_UNUSED_ARG(ipaddr);
+
+ 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;
+}
+
+/**
+ * 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*
+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
+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
+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
+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;
+ 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
+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
+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
+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
+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
+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/Libraries/Lwip/lwip/test/unit/core/test_mem.c b/Libraries/Lwip/lwip/test/unit/core/test_mem.c
new file mode 100644
index 00000000..d3a5d540
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/core/test_mem.c
@@ -0,0 +1,73 @@
+#include "test_mem.h"
+
+#include "lwip/mem.h"
+#include "lwip/stats.h"
+
+#if !LWIP_STATS || !MEM_STATS
+#error "This tests needs MEM-statistics enabled"
+#endif
+#if LWIP_DNS
+#error "This test needs DNS turned off (as it mallocs on init)"
+#endif
+
+/* Setups/teardown functions */
+
+static void
+mem_setup(void)
+{
+}
+
+static void
+mem_teardown(void)
+{
+}
+
+
+/* Test functions */
+
+/** Call mem_malloc, mem_free and mem_trim and check stats */
+START_TEST(test_mem_one)
+{
+#define SIZE1 16
+#define SIZE1_2 12
+#define SIZE2 16
+ void *p1, *p2;
+ mem_size_t s1, s2;
+ LWIP_UNUSED_ARG(_i);
+
+#if LWIP_DNS
+ fail("This test needs DNS turned off (as it mallocs on init)");
+#endif
+
+ fail_unless(lwip_stats.mem.used == 0);
+
+ p1 = mem_malloc(SIZE1);
+ fail_unless(p1 != NULL);
+ fail_unless(lwip_stats.mem.used >= SIZE1);
+ s1 = lwip_stats.mem.used;
+
+ p2 = mem_malloc(SIZE2);
+ fail_unless(p2 != NULL);
+ fail_unless(lwip_stats.mem.used >= SIZE2 + s1);
+ s2 = lwip_stats.mem.used;
+
+ mem_trim(p1, SIZE1_2);
+
+ mem_free(p2);
+ fail_unless(lwip_stats.mem.used <= s2 - SIZE2);
+
+ mem_free(p1);
+ fail_unless(lwip_stats.mem.used == 0);
+}
+END_TEST
+
+
+/** Create the suite including all tests for this module */
+Suite *
+mem_suite(void)
+{
+ TFun tests[] = {
+ test_mem_one
+ };
+ return create_suite("MEM", tests, sizeof(tests)/sizeof(TFun), mem_setup, mem_teardown);
+}
diff --git a/Libraries/Lwip/lwip/test/unit/core/test_mem.h b/Libraries/Lwip/lwip/test/unit/core/test_mem.h
new file mode 100644
index 00000000..13803edc
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/core/test_mem.h
@@ -0,0 +1,8 @@
+#ifndef __TEST_MEM_H__
+#define __TEST_MEM_H__
+
+#include "../lwip_check.h"
+
+Suite *mem_suite(void);
+
+#endif
diff --git a/Libraries/Lwip/lwip/test/unit/etharp/test_etharp.c b/Libraries/Lwip/lwip/test/unit/etharp/test_etharp.c
new file mode 100644
index 00000000..cbbc9502
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/etharp/test_etharp.c
@@ -0,0 +1,262 @@
+#include "test_etharp.h"
+
+#include "lwip/udp.h"
+#include "netif/etharp.h"
+#include "lwip/stats.h"
+
+#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS
+#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled"
+#endif
+#if !ETHARP_SUPPORT_STATIC_ENTRIES
+#error "This test needs ETHARP_SUPPORT_STATIC_ENTRIES enabled"
+#endif
+
+static struct netif test_netif;
+static ip_addr_t test_ipaddr, test_netmask, test_gw;
+struct eth_addr test_ethaddr = {1,1,1,1,1,1};
+struct eth_addr test_ethaddr2 = {1,1,1,1,1,2};
+struct eth_addr test_ethaddr3 = {1,1,1,1,1,3};
+struct eth_addr test_ethaddr4 = {1,1,1,1,1,4};
+static int linkoutput_ctr;
+
+/* Helper functions */
+static void
+etharp_remove_all(void)
+{
+ int i;
+ /* call etharp_tmr often enough to have all entries cleaned */
+ for(i = 0; i < 0xff; i++) {
+ etharp_tmr();
+ }
+}
+
+static err_t
+default_netif_linkoutput(struct netif *netif, struct pbuf *p)
+{
+ fail_unless(netif == &test_netif);
+ fail_unless(p != NULL);
+ linkoutput_ctr++;
+ return ERR_OK;
+}
+
+static err_t
+default_netif_init(struct netif *netif)
+{
+ fail_unless(netif != NULL);
+ netif->linkoutput = default_netif_linkoutput;
+ netif->output = etharp_output;
+ netif->mtu = 1500;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+ return ERR_OK;
+}
+
+static void
+default_netif_add(void)
+{
+ IP4_ADDR(&test_gw, 192,168,0,1);
+ IP4_ADDR(&test_ipaddr, 192,168,0,1);
+ IP4_ADDR(&test_netmask, 255,255,0,0);
+
+ fail_unless(netif_default == NULL);
+ netif_set_default(netif_add(&test_netif, &test_ipaddr, &test_netmask,
+ &test_gw, NULL, default_netif_init, NULL));
+ netif_set_up(&test_netif);
+}
+
+static void
+default_netif_remove(void)
+{
+ fail_unless(netif_default == &test_netif);
+ netif_remove(&test_netif);
+}
+
+static void
+create_arp_response(ip_addr_t *adr)
+{
+ int k;
+ struct eth_hdr *ethhdr;
+ struct etharp_hdr *etharphdr;
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(struct eth_hdr) + sizeof(struct etharp_hdr), PBUF_RAM);
+ if(p == NULL) {
+ FAIL_RET();
+ }
+ ethhdr = (struct eth_hdr*)p->payload;
+ etharphdr = (struct etharp_hdr*)(ethhdr + 1);
+
+ ethhdr->dest = test_ethaddr;
+ ethhdr->src = test_ethaddr2;
+ ethhdr->type = htons(ETHTYPE_ARP);
+
+ etharphdr->hwtype = htons(/*HWTYPE_ETHERNET*/ 1);
+ etharphdr->proto = htons(ETHTYPE_IP);
+ etharphdr->hwlen = ETHARP_HWADDR_LEN;
+ etharphdr->protolen = sizeof(ip_addr_t);
+ etharphdr->opcode = htons(ARP_REPLY);
+
+ SMEMCPY(&etharphdr->sipaddr, adr, sizeof(ip_addr_t));
+ SMEMCPY(&etharphdr->dipaddr, &test_ipaddr, sizeof(ip_addr_t));
+
+ k = 6;
+ while(k > 0) {
+ k--;
+ /* Write the ARP MAC-Addresses */
+ etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k];
+ etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k];
+ /* Write the Ethernet MAC-Addresses */
+ ethhdr->dest.addr[k] = test_ethaddr.addr[k];
+ ethhdr->src.addr[k] = test_ethaddr2.addr[k];
+ }
+
+ ethernet_input(p, &test_netif);
+}
+
+/* Setups/teardown functions */
+
+static void
+etharp_setup(void)
+{
+ etharp_remove_all();
+ default_netif_add();
+}
+
+static void
+etharp_teardown(void)
+{
+ etharp_remove_all();
+ default_netif_remove();
+}
+
+
+/* Test functions */
+
+START_TEST(test_etharp_table)
+{
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ err_t err;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ s8_t idx;
+ ip_addr_t *unused_ipaddr;
+ struct eth_addr *unused_ethaddr;
+ struct udp_pcb* pcb;
+ LWIP_UNUSED_ARG(_i);
+
+ if (netif_default != &test_netif) {
+ fail("This test needs a default netif");
+ }
+
+ linkoutput_ctr = 0;
+
+ pcb = udp_new();
+ fail_unless(pcb != NULL);
+ if (pcb != NULL) {
+ ip_addr_t adrs[ARP_TABLE_SIZE + 2];
+ int i;
+ for(i = 0; i < ARP_TABLE_SIZE + 2; i++) {
+ IP4_ADDR(&adrs[i], 192,168,0,i+2);
+ }
+ /* fill ARP-table with dynamic entries */
+ for(i = 0; i < ARP_TABLE_SIZE; i++) {
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ err_t err = udp_sendto(pcb, p, &adrs[i], 123);
+ fail_unless(err == ERR_OK);
+ /* etharp request sent? */
+ fail_unless(linkoutput_ctr == (2*i) + 1);
+ pbuf_free(p);
+
+ /* create an ARP response */
+ create_arp_response(&adrs[i]);
+ /* queued UDP packet sent? */
+ fail_unless(linkoutput_ctr == (2*i) + 2);
+
+ idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == i);
+ etharp_tmr();
+ }
+ }
+ linkoutput_ctr = 0;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* create one static entry */
+ err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3);
+ fail_unless(err == ERR_OK);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == 0);
+ fail_unless(linkoutput_ctr == 0);
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+ linkoutput_ctr = 0;
+ /* fill ARP-table with dynamic entries */
+ for(i = 0; i < ARP_TABLE_SIZE; i++) {
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ err_t err = udp_sendto(pcb, p, &adrs[i], 123);
+ fail_unless(err == ERR_OK);
+ /* etharp request sent? */
+ fail_unless(linkoutput_ctr == (2*i) + 1);
+ pbuf_free(p);
+
+ /* create an ARP response */
+ create_arp_response(&adrs[i]);
+ /* queued UDP packet sent? */
+ fail_unless(linkoutput_ctr == (2*i) + 2);
+
+ idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr);
+ if (i < ARP_TABLE_SIZE - 1) {
+ fail_unless(idx == i+1);
+ } else {
+ /* the last entry must not overwrite the static entry! */
+ fail_unless(idx == 1);
+ }
+ etharp_tmr();
+ }
+ }
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* create a second static entry */
+ err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4);
+ fail_unless(err == ERR_OK);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == 0);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == 2);
+ /* and remove it again */
+ err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]);
+ fail_unless(err == ERR_OK);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == 0);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == -1);
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+ /* check that static entries don't time out */
+ etharp_remove_all();
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == 0);
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* remove the first static entry */
+ err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]);
+ fail_unless(err == ERR_OK);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == -1);
+ idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr);
+ fail_unless(idx == -1);
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+ udp_remove(pcb);
+ }
+}
+END_TEST
+
+
+/** Create the suite including all tests for this module */
+Suite *
+etharp_suite(void)
+{
+ TFun tests[] = {
+ test_etharp_table
+ };
+ return create_suite("ETHARP", tests, sizeof(tests)/sizeof(TFun), etharp_setup, etharp_teardown);
+}
diff --git a/Libraries/Lwip/lwip/test/unit/etharp/test_etharp.h b/Libraries/Lwip/lwip/test/unit/etharp/test_etharp.h
new file mode 100644
index 00000000..96e00c3b
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/etharp/test_etharp.h
@@ -0,0 +1,8 @@
+#ifndef __TEST_ETHARP_H__
+#define __TEST_ETHARP_H__
+
+#include "../lwip_check.h"
+
+Suite* etharp_suite(void);
+
+#endif
diff --git a/Libraries/Lwip/lwip/test/unit/lwip_check.h b/Libraries/Lwip/lwip/test/unit/lwip_check.h
new file mode 100644
index 00000000..e27f55ae
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/lwip_check.h
@@ -0,0 +1,37 @@
+#ifndef __LWIP_CHECK_H__
+#define __LWIP_CHECK_H__
+
+/* Common header file for lwIP unit tests using the check framework */
+
+#include <config.h>
+#include <check.h>
+#include <stdlib.h>
+
+#define FAIL_RET() do { fail(); return; } while(0)
+#define EXPECT(x) fail_unless(x)
+#define EXPECT_RET(x) do { fail_unless(x); if(!(x)) { return; }} while(0)
+#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0)
+#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL)
+
+/** typedef for a function returning a test suite */
+typedef Suite* (suite_getter_fn)(void);
+
+/** Create a test suite */
+static Suite* create_suite(const char* name, TFun *tests, size_t num_tests, SFun setup, SFun teardown)
+{
+ size_t i;
+ Suite *s = suite_create(name);
+
+ for(i = 0; i < num_tests; i++) {
+ /* Core test case */
+ TCase *tc_core = tcase_create("Core");
+ if ((setup != NULL) || (teardown != NULL)) {
+ tcase_add_checked_fixture(tc_core, setup, teardown);
+ }
+ tcase_add_test(tc_core, tests[i]);
+ suite_add_tcase(s, tc_core);
+ }
+ return s;
+}
+
+#endif /* __LWIP_CHECK_H__ */
diff --git a/Libraries/Lwip/lwip/test/unit/lwip_unittests.c b/Libraries/Lwip/lwip/test/unit/lwip_unittests.c
new file mode 100644
index 00000000..4f537e66
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/lwip_unittests.c
@@ -0,0 +1,45 @@
+#include "lwip_check.h"
+
+#include "udp/test_udp.h"
+#include "tcp/test_tcp.h"
+#include "tcp/test_tcp_oos.h"
+#include "core/test_mem.h"
+#include "etharp/test_etharp.h"
+
+#include "lwip/init.h"
+
+
+int main()
+{
+ int number_failed;
+ SRunner *sr;
+ size_t i;
+ suite_getter_fn* suites[] = {
+ udp_suite,
+ tcp_suite,
+ tcp_oos_suite,
+ mem_suite,
+ etharp_suite
+ };
+ size_t num = sizeof(suites)/sizeof(void*);
+ LWIP_ASSERT("No suites defined", num > 0);
+
+ lwip_init();
+
+ sr = srunner_create((suites[0])());
+ for(i = 1; i < num; i++) {
+ srunner_add_suite(sr, ((suite_getter_fn*)suites[i])());
+ }
+
+#ifdef LWIP_UNITTESTS_NOFORK
+ srunner_set_fork_status(sr, CK_NOFORK);
+#endif
+#ifdef LWIP_UNITTESTS_FORK
+ srunner_set_fork_status(sr, CK_FORK);
+#endif
+
+ srunner_run_all(sr, CK_NORMAL);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/Libraries/Lwip/lwip/test/unit/lwipopts.h b/Libraries/Lwip/lwip/test/unit/lwipopts.h
new file mode 100644
index 00000000..88e76d7a
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/lwipopts.h
@@ -0,0 +1,50 @@
+/*
+ * 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: Simon Goldschmidt
+ *
+ */
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
+#define NO_SYS 1
+#define LWIP_NETCONN 0
+#define LWIP_SOCKET 0
+
+/* Minimal changes to opt.h required for tcp unit tests: */
+#define MEM_SIZE 16000
+#define TCP_SND_QUEUELEN 40
+#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
+#define TCP_SND_BUF (12 * TCP_MSS)
+#define TCP_WND (10 * TCP_MSS)
+
+/* Minimal changes to opt.h required for etharp unit tests: */
+#define ETHARP_SUPPORT_STATIC_ENTRIES 1
+
+#endif /* __LWIPOPTS_H__ */
diff --git a/Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.c b/Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.c
new file mode 100644
index 00000000..dd550f1b
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.c
@@ -0,0 +1,294 @@
+#include "tcp_helper.h"
+
+#include "lwip/tcp_impl.h"
+#include "lwip/stats.h"
+#include "lwip/pbuf.h"
+#include "lwip/inet_chksum.h"
+
+#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
+#error "This tests needs TCP- and MEMP-statistics enabled"
+#endif
+
+/** Remove all pcbs on the given list. */
+static void
+tcp_remove(struct tcp_pcb* pcb_list)
+{
+ struct tcp_pcb *pcb = pcb_list;
+ struct tcp_pcb *pcb2;
+
+ while(pcb != NULL) {
+ pcb2 = pcb;
+ pcb = pcb->next;
+ tcp_abort(pcb2);
+ }
+}
+
+/** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */
+void
+tcp_remove_all(void)
+{
+ tcp_remove(tcp_listen_pcbs.pcbs);
+ tcp_remove(tcp_active_pcbs);
+ tcp_remove(tcp_tw_pcbs);
+ fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ fail_unless(lwip_stats.memp[MEMP_TCP_PCB_LISTEN].used == 0);
+ fail_unless(lwip_stats.memp[MEMP_TCP_SEG].used == 0);
+ fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0);
+}
+
+/** Create a TCP segment usable for passing to tcp_input */
+static struct pbuf*
+tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
+ u16_t src_port, u16_t dst_port, void* data, size_t data_len,
+ u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd)
+{
+ struct pbuf *p, *q;
+ struct ip_hdr* iphdr;
+ struct tcp_hdr* tcphdr;
+ u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);
+
+ p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
+ EXPECT_RETNULL(p != NULL);
+ /* first pbuf must be big enough to hold the headers */
+ EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
+ if (data_len > 0) {
+ /* first pbuf must be big enough to hold at least 1 data byte, too */
+ EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
+ }
+
+ for(q = p; q != NULL; q = q->next) {
+ memset(q->payload, 0, q->len);
+ }
+
+ iphdr = p->payload;
+ /* fill IP header */
+ iphdr->dest.addr = dst_ip->addr;
+ iphdr->src.addr = src_ip->addr;
+ IPH_VHL_SET(iphdr, 4, IP_HLEN / 4);
+ IPH_TOS_SET(iphdr, 0);
+ IPH_LEN_SET(iphdr, htons(p->tot_len));
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+
+ /* let p point to TCP header */
+ pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
+
+ tcphdr = p->payload;
+ tcphdr->src = htons(src_port);
+ tcphdr->dest = htons(dst_port);
+ tcphdr->seqno = htonl(seqno);
+ tcphdr->ackno = htonl(ackno);
+ TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4);
+ TCPH_FLAGS_SET(tcphdr, headerflags);
+ tcphdr->wnd = htons(wnd);
+
+ if (data_len > 0) {
+ /* let p point to TCP data */
+ pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr));
+ /* copy data */
+ pbuf_take(p, data, data_len);
+ /* let p point to TCP header again */
+ pbuf_header(p, sizeof(struct tcp_hdr));
+ }
+
+ /* calculate checksum */
+
+ tcphdr->chksum = inet_chksum_pseudo(p, src_ip, dst_ip,
+ IP_PROTO_TCP, p->tot_len);
+
+ pbuf_header(p, sizeof(struct ip_hdr));
+
+ return p;
+}
+
+/** Create a TCP segment usable for passing to tcp_input */
+struct pbuf*
+tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
+ u16_t src_port, u16_t dst_port, void* data, size_t data_len,
+ u32_t seqno, u32_t ackno, u8_t headerflags)
+{
+ return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data,
+ data_len, seqno, ackno, headerflags, TCP_WND);
+}
+
+/** Create a TCP segment usable for passing to tcp_input
+ * - IP-addresses, ports, seqno and ackno are taken from pcb
+ * - seqno and ackno can be altered with an offset
+ */
+struct pbuf*
+tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset,
+ u32_t ackno_offset, u8_t headerflags)
+{
+ return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
+ data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags);
+}
+
+/** Create a TCP segment usable for passing to tcp_input
+ * - IP-addresses, ports, seqno and ackno are taken from pcb
+ * - seqno and ackno can be altered with an offset
+ * - TCP window can be adjusted
+ */
+struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
+ u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd)
+{
+ return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
+ data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd);
+}
+
+/** Safely bring a tcp_pcb into the requested state */
+void
+tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
+ ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
+{
+ /* @todo: are these all states? */
+ /* @todo: remove from previous list */
+ pcb->state = state;
+ if (state == ESTABLISHED) {
+ TCP_REG(&tcp_active_pcbs, pcb);
+ pcb->local_ip.addr = local_ip->addr;
+ pcb->local_port = local_port;
+ pcb->remote_ip.addr = remote_ip->addr;
+ pcb->remote_port = remote_port;
+ } else if(state == LISTEN) {
+ TCP_REG(&tcp_listen_pcbs.pcbs, pcb);
+ pcb->local_ip.addr = local_ip->addr;
+ pcb->local_port = local_port;
+ } else if(state == TIME_WAIT) {
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ pcb->local_ip.addr = local_ip->addr;
+ pcb->local_port = local_port;
+ pcb->remote_ip.addr = remote_ip->addr;
+ pcb->remote_port = remote_port;
+ } else {
+ fail();
+ }
+}
+
+void
+test_tcp_counters_err(void* arg, err_t err)
+{
+ struct test_tcp_counters* counters = arg;
+ EXPECT_RET(arg != NULL);
+ counters->err_calls++;
+ counters->last_err = err;
+}
+
+static void
+test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p)
+{
+ struct pbuf* q;
+ u32_t i, received;
+ if(counters->expected_data == NULL) {
+ /* no data to compare */
+ return;
+ }
+ EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len);
+ received = counters->recved_bytes;
+ for(q = p; q != NULL; q = q->next) {
+ char *data = q->payload;
+ for(i = 0; i < q->len; i++) {
+ EXPECT_RET(data[i] == counters->expected_data[received]);
+ received++;
+ }
+ }
+ EXPECT(received == counters->recved_bytes + p->tot_len);
+}
+
+err_t
+test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
+{
+ struct test_tcp_counters* counters = arg;
+ EXPECT_RETX(arg != NULL, ERR_OK);
+ EXPECT_RETX(pcb != NULL, ERR_OK);
+ EXPECT_RETX(err == ERR_OK, ERR_OK);
+
+ if (p != NULL) {
+ if (counters->close_calls == 0) {
+ counters->recv_calls++;
+ test_tcp_counters_check_rxdata(counters, p);
+ counters->recved_bytes += p->tot_len;
+ } else {
+ counters->recv_calls_after_close++;
+ counters->recved_bytes_after_close += p->tot_len;
+ }
+ pbuf_free(p);
+ } else {
+ counters->close_calls++;
+ }
+ EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0);
+ return ERR_OK;
+}
+
+/** Allocate a pcb and set up the test_tcp_counters_* callbacks */
+struct tcp_pcb*
+test_tcp_new_counters_pcb(struct test_tcp_counters* counters)
+{
+ struct tcp_pcb* pcb = tcp_new();
+ if (pcb != NULL) {
+ /* set up args and callbacks */
+ tcp_arg(pcb, counters);
+ tcp_recv(pcb, test_tcp_counters_recv);
+ tcp_err(pcb, test_tcp_counters_err);
+ pcb->snd_wnd = TCP_WND;
+ pcb->snd_wnd_max = TCP_WND;
+ }
+ return pcb;
+}
+
+/** Calls tcp_input() after adjusting current_iphdr_dest */
+void test_tcp_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
+ ip_addr_copy(current_iphdr_dest, iphdr->dest);
+ ip_addr_copy(current_iphdr_src, iphdr->src);
+ current_netif = inp;
+ current_header = iphdr;
+
+ tcp_input(p, inp);
+
+ current_iphdr_dest.addr = 0;
+ current_iphdr_src.addr = 0;
+ current_netif = NULL;
+ current_header = NULL;
+}
+
+static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
+ ip_addr_t *ipaddr)
+{
+ struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state;
+ LWIP_UNUSED_ARG(ipaddr);
+ txcounters->num_tx_calls++;
+ txcounters->num_tx_bytes += p->tot_len;
+ if (txcounters->copy_tx_packets) {
+ struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ err_t err;
+ EXPECT(p_copy != NULL);
+ err = pbuf_copy(p_copy, p);
+ EXPECT(err == ERR_OK);
+ if (txcounters->tx_packets == NULL) {
+ txcounters->tx_packets = p_copy;
+ } else {
+ pbuf_cat(txcounters->tx_packets, p_copy);
+ }
+ }
+ return ERR_OK;
+}
+
+void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
+ ip_addr_t *ip_addr, ip_addr_t *netmask)
+{
+ struct netif *n;
+ memset(netif, 0, sizeof(struct netif));
+ memset(txcounters, 0, sizeof(struct test_tcp_txcounters));
+ netif->output = test_tcp_netif_output;
+ netif->state = txcounters;
+ netif->flags |= NETIF_FLAG_UP;
+ ip_addr_copy(netif->netmask, *netmask);
+ ip_addr_copy(netif->ip_addr, *ip_addr);
+ for (n = netif_list; n != NULL; n = n->next) {
+ if (n == netif) {
+ return;
+ }
+ }
+ netif->next = NULL;
+ netif_list = netif;
+}
diff --git a/Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.h b/Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.h
new file mode 100644
index 00000000..4a72c935
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/tcp/tcp_helper.h
@@ -0,0 +1,52 @@
+#ifndef __TCP_HELPER_H__
+#define __TCP_HELPER_H__
+
+#include "../lwip_check.h"
+#include "lwip/arch.h"
+#include "lwip/tcp.h"
+#include "lwip/netif.h"
+
+/* counters used for test_tcp_counters_* callback functions */
+struct test_tcp_counters {
+ u32_t recv_calls;
+ u32_t recved_bytes;
+ u32_t recv_calls_after_close;
+ u32_t recved_bytes_after_close;
+ u32_t close_calls;
+ u32_t err_calls;
+ err_t last_err;
+ char* expected_data;
+ u32_t expected_data_len;
+};
+
+struct test_tcp_txcounters {
+ u32_t num_tx_calls;
+ u32_t num_tx_bytes;
+ u8_t copy_tx_packets;
+ struct pbuf *tx_packets;
+};
+
+/* Helper functions */
+void tcp_remove_all(void);
+
+struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
+ u16_t src_port, u16_t dst_port, void* data, size_t data_len,
+ u32_t seqno, u32_t ackno, u8_t headerflags);
+struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len,
+ u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags);
+struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
+ u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd);
+void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
+ ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port);
+void test_tcp_counters_err(void* arg, err_t err);
+err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
+
+struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters);
+
+void test_tcp_input(struct pbuf *p, struct netif *inp);
+
+void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
+ ip_addr_t *ip_addr, ip_addr_t *netmask);
+
+
+#endif
diff --git a/Libraries/Lwip/lwip/test/unit/tcp/test_tcp.c b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp.c
new file mode 100644
index 00000000..6fd5be50
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp.c
@@ -0,0 +1,667 @@
+#include "test_tcp.h"
+
+#include "lwip/tcp_impl.h"
+#include "lwip/stats.h"
+#include "tcp_helper.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */
+#endif
+
+#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
+#error "This tests needs TCP- and MEMP-statistics enabled"
+#endif
+#if TCP_SND_BUF <= TCP_WND
+#error "This tests needs TCP_SND_BUF to be > TCP_WND"
+#endif
+
+static u8_t test_tcp_timer;
+
+/* our own version of tcp_tmr so we can reset fast/slow timer state */
+static void
+test_tcp_tmr(void)
+{
+ tcp_fasttmr();
+ if (++test_tcp_timer & 1) {
+ tcp_slowtmr();
+ }
+}
+
+/* Setups/teardown functions */
+
+static void
+tcp_setup(void)
+{
+ /* reset iss to default (6510) */
+ tcp_ticks = 0;
+ tcp_ticks = 0 - (tcp_next_iss() - 6510);
+ tcp_next_iss();
+ tcp_ticks = 0;
+
+ test_tcp_timer = 0;
+ tcp_remove_all();
+}
+
+static void
+tcp_teardown(void)
+{
+ netif_list = NULL;
+ tcp_remove_all();
+}
+
+
+/* Test functions */
+
+/** Call tcp_new() and tcp_abort() and test memp stats */
+START_TEST(test_tcp_new_abort)
+{
+ struct tcp_pcb* pcb;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+
+ pcb = tcp_new();
+ fail_unless(pcb != NULL);
+ if (pcb != NULL) {
+ fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ }
+}
+END_TEST
+
+/** Create an ESTABLISHED pcb and check if receive callback is called */
+START_TEST(test_tcp_recv_inseq)
+{
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ char data[] = {1, 2, 3, 4};
+ ip_addr_t remote_ip, local_ip;
+ u16_t data_len;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ data_len = sizeof(data);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = data_len;
+ counters.expected_data = data;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+
+ /* create a segment */
+ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
+ EXPECT(p != NULL);
+ if (p != NULL) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 1);
+ EXPECT(counters.recved_bytes == data_len);
+ EXPECT(counters.err_calls == 0);
+ }
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+END_TEST
+
+/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
+ * At the end, send more data. */
+START_TEST(test_tcp_fast_retx_recover)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ char data1[] = { 1, 2, 3, 4};
+ char data2[] = { 5, 6, 7, 8};
+ char data3[] = { 9, 10, 11, 12};
+ char data4[] = {13, 14, 15, 16};
+ char data5[] = {17, 18, 19, 20};
+ char data6[] = {21, 22, 23, 24};
+ ip_addr_t remote_ip, local_ip, netmask;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ err_t err;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->mss = TCP_MSS;
+ /* disable initial congestion window (we don't send a SYN here...) */
+ pcb->cwnd = pcb->snd_wnd;
+
+ /* send data1 */
+ err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ EXPECT_RET(txcounters.num_tx_calls == 1);
+ EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
+ memset(&txcounters, 0, sizeof(txcounters));
+ /* "recv" ACK for data1 */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ test_tcp_input(p, &netif);
+ EXPECT_RET(txcounters.num_tx_calls == 0);
+ EXPECT_RET(pcb->unacked == NULL);
+ /* send data2 */
+ err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ EXPECT_RET(txcounters.num_tx_calls == 1);
+ EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
+ memset(&txcounters, 0, sizeof(txcounters));
+ /* duplicate ACK for data1 (data2 is lost) */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ test_tcp_input(p, &netif);
+ EXPECT_RET(txcounters.num_tx_calls == 0);
+ EXPECT_RET(pcb->dupacks == 1);
+ /* send data3 */
+ err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ /* nagle enabled, no tx calls */
+ EXPECT_RET(txcounters.num_tx_calls == 0);
+ EXPECT_RET(txcounters.num_tx_bytes == 0);
+ memset(&txcounters, 0, sizeof(txcounters));
+ /* 2nd duplicate ACK for data1 (data2 and data3 are lost) */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ test_tcp_input(p, &netif);
+ EXPECT_RET(txcounters.num_tx_calls == 0);
+ EXPECT_RET(pcb->dupacks == 2);
+ /* queue data4, don't send it (unsent-oversize is != 0) */
+ err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ /* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ test_tcp_input(p, &netif);
+ /*EXPECT_RET(txcounters.num_tx_calls == 1);*/
+ EXPECT_RET(pcb->dupacks == 3);
+ memset(&txcounters, 0, sizeof(txcounters));
+ /* TODO: check expected data?*/
+
+ /* send data5, not output yet */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ /*err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);*/
+ EXPECT_RET(txcounters.num_tx_calls == 0);
+ EXPECT_RET(txcounters.num_tx_bytes == 0);
+ memset(&txcounters, 0, sizeof(txcounters));
+ {
+ int i = 0;
+ do
+ {
+ err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY);
+ i++;
+ }while(err == ERR_OK);
+ EXPECT_RET(err != ERR_OK);
+ }
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ /*EXPECT_RET(txcounters.num_tx_calls == 0);
+ EXPECT_RET(txcounters.num_tx_bytes == 0);*/
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* send even more data */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ /* ...and even more data */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ /* ...and even more data */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ /* ...and even more data */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+
+ /* send ACKs for data2 and data3 */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ test_tcp_input(p, &netif);
+ /*EXPECT_RET(txcounters.num_tx_calls == 0);*/
+
+ /* ...and even more data */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ /* ...and even more data */
+ err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+
+#if 0
+ /* create expected segment */
+ p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
+ EXPECT_RET(p != NULL);
+ if (p != NULL) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT_RET(counters.close_calls == 0);
+ EXPECT_RET(counters.recv_calls == 1);
+ EXPECT_RET(counters.recved_bytes == data_len);
+ EXPECT_RET(counters.err_calls == 0);
+ }
+#endif
+ /* make sure the pcb is freed */
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+END_TEST
+
+static u8_t tx_data[TCP_WND*2];
+
+static void
+check_seqnos(struct tcp_seg *segs, int num_expected, u32_t *seqnos_expected)
+{
+ struct tcp_seg *s = segs;
+ int i;
+ for (i = 0; i < num_expected; i++, s = s->next) {
+ EXPECT_RET(s != NULL);
+ EXPECT(s->tcphdr->seqno == htonl(seqnos_expected[i]));
+ }
+ EXPECT(s == NULL);
+}
+
+/** Send data with sequence numbers that wrap around the u32_t range.
+ * Then, provoke fast retransmission by duplicate ACKs and check that all
+ * segment lists are still properly sorted. */
+START_TEST(test_tcp_fast_rexmit_wraparound)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ ip_addr_t remote_ip, local_ip, netmask;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ err_t err;
+#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
+#define ISS 6510
+ u16_t i, sent_total = 0;
+ u32_t seqnos[] = {
+ SEQNO1,
+ SEQNO1 + (1 * TCP_MSS),
+ SEQNO1 + (2 * TCP_MSS),
+ SEQNO1 + (3 * TCP_MSS),
+ SEQNO1 + (4 * TCP_MSS),
+ SEQNO1 + (5 * TCP_MSS)};
+ LWIP_UNUSED_ARG(_i);
+
+ for (i = 0; i < sizeof(tx_data); i++) {
+ tx_data[i] = (u8_t)i;
+ }
+
+ /* initialize local vars */
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ tcp_ticks = SEQNO1 - ISS;
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ EXPECT(pcb->lastack == SEQNO1);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->mss = TCP_MSS;
+ /* disable initial congestion window (we don't send a SYN here...) */
+ pcb->cwnd = 2*TCP_MSS;
+
+ /* send 6 mss-sized segments */
+ for (i = 0; i < 6; i++) {
+ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ sent_total += TCP_MSS;
+ }
+ check_seqnos(pcb->unsent, 6, seqnos);
+ EXPECT(pcb->unacked == NULL);
+ err = tcp_output(pcb);
+ EXPECT(txcounters.num_tx_calls == 2);
+ EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ check_seqnos(pcb->unacked, 2, seqnos);
+ check_seqnos(pcb->unsent, 4, &seqnos[2]);
+
+ /* ACK the first segment */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK);
+ test_tcp_input(p, &netif);
+ /* ensure this didn't trigger a retransmission */
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
+ memset(&txcounters, 0, sizeof(txcounters));
+ check_seqnos(pcb->unacked, 2, &seqnos[1]);
+ check_seqnos(pcb->unsent, 3, &seqnos[3]);
+
+ /* 3 dupacks */
+ EXPECT(pcb->dupacks == 0);
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ test_tcp_input(p, &netif);
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(pcb->dupacks == 1);
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ test_tcp_input(p, &netif);
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(pcb->dupacks == 2);
+ /* 3rd dupack -> fast rexmit */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ test_tcp_input(p, &netif);
+ EXPECT(pcb->dupacks == 3);
+ EXPECT(txcounters.num_tx_calls == 4);
+ memset(&txcounters, 0, sizeof(txcounters));
+ EXPECT(pcb->unsent == NULL);
+ check_seqnos(pcb->unacked, 5, &seqnos[1]);
+
+ /* make sure the pcb is freed */
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+END_TEST
+
+/** Send data with sequence numbers that wrap around the u32_t range.
+ * Then, provoke RTO retransmission and check that all
+ * segment lists are still properly sorted. */
+START_TEST(test_tcp_rto_rexmit_wraparound)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ ip_addr_t remote_ip, local_ip, netmask;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ err_t err;
+#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
+#define ISS 6510
+ u16_t i, sent_total = 0;
+ u32_t seqnos[] = {
+ SEQNO1,
+ SEQNO1 + (1 * TCP_MSS),
+ SEQNO1 + (2 * TCP_MSS),
+ SEQNO1 + (3 * TCP_MSS),
+ SEQNO1 + (4 * TCP_MSS),
+ SEQNO1 + (5 * TCP_MSS)};
+ LWIP_UNUSED_ARG(_i);
+
+ for (i = 0; i < sizeof(tx_data); i++) {
+ tx_data[i] = (u8_t)i;
+ }
+
+ /* initialize local vars */
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ tcp_ticks = 0;
+ tcp_ticks = 0 - tcp_next_iss();
+ tcp_ticks = SEQNO1 - tcp_next_iss();
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ EXPECT(pcb->lastack == SEQNO1);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->mss = TCP_MSS;
+ /* disable initial congestion window (we don't send a SYN here...) */
+ pcb->cwnd = 2*TCP_MSS;
+
+ /* send 6 mss-sized segments */
+ for (i = 0; i < 6; i++) {
+ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ sent_total += TCP_MSS;
+ }
+ check_seqnos(pcb->unsent, 6, seqnos);
+ EXPECT(pcb->unacked == NULL);
+ err = tcp_output(pcb);
+ EXPECT(txcounters.num_tx_calls == 2);
+ EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ check_seqnos(pcb->unacked, 2, seqnos);
+ check_seqnos(pcb->unsent, 4, &seqnos[2]);
+
+ /* call the tcp timer some times */
+ for (i = 0; i < 10; i++) {
+ test_tcp_tmr();
+ EXPECT(txcounters.num_tx_calls == 0);
+ }
+ /* 11th call to tcp_tmr: RTO rexmit fires */
+ test_tcp_tmr();
+ EXPECT(txcounters.num_tx_calls == 1);
+ check_seqnos(pcb->unacked, 1, seqnos);
+ check_seqnos(pcb->unsent, 5, &seqnos[1]);
+
+ /* fake greater cwnd */
+ pcb->cwnd = pcb->snd_wnd;
+ /* send more data */
+ err = tcp_output(pcb);
+ EXPECT(err == ERR_OK);
+ /* check queues are sorted */
+ EXPECT(pcb->unsent == NULL);
+ check_seqnos(pcb->unacked, 6, seqnos);
+
+ /* make sure the pcb is freed */
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+END_TEST
+
+/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
+ * At the end, send more data. */
+static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *p;
+ ip_addr_t remote_ip, local_ip, netmask;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ err_t err;
+ u16_t sent_total, i;
+ u8_t expected = 0xFE;
+
+ for (i = 0; i < sizeof(tx_data); i++) {
+ u8_t d = (u8_t)i;
+ if (d == 0xFE) {
+ d = 0xF0;
+ }
+ tx_data[i] = d;
+ }
+ if (zero_window_probe_from_unsent) {
+ tx_data[TCP_WND] = expected;
+ } else {
+ tx_data[0] = expected;
+ }
+
+ /* initialize local vars */
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ memset(&counters, 0, sizeof(counters));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->mss = TCP_MSS;
+ /* disable initial congestion window (we don't send a SYN here...) */
+ pcb->cwnd = pcb->snd_wnd;
+
+ /* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */
+ sent_total = 0;
+ if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) {
+ u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS;
+ err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U);
+ memset(&txcounters, 0, sizeof(txcounters));
+ sent_total += initial_data_len;
+ }
+ for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) {
+ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
+ memset(&txcounters, 0, sizeof(txcounters));
+ }
+ EXPECT(sent_total == (TCP_WND - TCP_MSS));
+
+ /* now ACK the packet before the first */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
+ test_tcp_input(p, &netif);
+ /* ensure this didn't trigger a retransmission */
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+
+ EXPECT(pcb->persist_backoff == 0);
+ /* send the last packet, now a complete window has been sent */
+ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ sent_total += TCP_MSS;
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
+ memset(&txcounters, 0, sizeof(txcounters));
+ EXPECT(pcb->persist_backoff == 0);
+
+ if (zero_window_probe_from_unsent) {
+ /* ACK all data but close the TX window */
+ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0);
+ test_tcp_input(p, &netif);
+ /* ensure this didn't trigger any transmission */
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ EXPECT(pcb->persist_backoff == 1);
+ }
+
+ /* send one byte more (out of window) -> persist timer starts */
+ err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT_RET(err == ERR_OK);
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ memset(&txcounters, 0, sizeof(txcounters));
+ if (!zero_window_probe_from_unsent) {
+ /* no persist timer unless a zero window announcement has been received */
+ EXPECT(pcb->persist_backoff == 0);
+ } else {
+ EXPECT(pcb->persist_backoff == 1);
+
+ /* call tcp_timer some more times to let persist timer count up */
+ for (i = 0; i < 4; i++) {
+ test_tcp_tmr();
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ }
+
+ /* this should trigger the zero-window-probe */
+ txcounters.copy_tx_packets = 1;
+ test_tcp_tmr();
+ txcounters.copy_tx_packets = 0;
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == 1 + 40U);
+ EXPECT(txcounters.tx_packets != NULL);
+ if (txcounters.tx_packets != NULL) {
+ u8_t sent;
+ u16_t ret;
+ ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U);
+ EXPECT(ret == 1);
+ EXPECT(sent == expected);
+ }
+ if (txcounters.tx_packets != NULL) {
+ pbuf_free(txcounters.tx_packets);
+ txcounters.tx_packets = NULL;
+ }
+ }
+
+ /* make sure the pcb is freed */
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+
+START_TEST(test_tcp_tx_full_window_lost_from_unsent)
+{
+ LWIP_UNUSED_ARG(_i);
+ test_tcp_tx_full_window_lost(1);
+}
+END_TEST
+
+START_TEST(test_tcp_tx_full_window_lost_from_unacked)
+{
+ LWIP_UNUSED_ARG(_i);
+ test_tcp_tx_full_window_lost(0);
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+tcp_suite(void)
+{
+ TFun tests[] = {
+ test_tcp_new_abort,
+ test_tcp_recv_inseq,
+ test_tcp_fast_retx_recover,
+ test_tcp_fast_rexmit_wraparound,
+ test_tcp_rto_rexmit_wraparound,
+ test_tcp_tx_full_window_lost_from_unacked,
+ test_tcp_tx_full_window_lost_from_unsent
+ };
+ return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown);
+}
diff --git a/Libraries/Lwip/lwip/test/unit/tcp/test_tcp.h b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp.h
new file mode 100644
index 00000000..f1c4a463
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp.h
@@ -0,0 +1,8 @@
+#ifndef __TEST_TCP_H__
+#define __TEST_TCP_H__
+
+#include "../lwip_check.h"
+
+Suite *tcp_suite(void);
+
+#endif
diff --git a/Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.c b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.c
new file mode 100644
index 00000000..764de1c4
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.c
@@ -0,0 +1,944 @@
+#include "test_tcp_oos.h"
+
+#include "lwip/tcp_impl.h"
+#include "lwip/stats.h"
+#include "tcp_helper.h"
+
+#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
+#error "This tests needs TCP- and MEMP-statistics enabled"
+#endif
+#if !TCP_QUEUE_OOSEQ
+#error "This tests needs TCP_QUEUE_OOSEQ enabled"
+#endif
+
+/** CHECK_SEGMENTS_ON_OOSEQ:
+ * 1: check count, seqno and len of segments on pcb->ooseq (strict)
+ * 0: only check that bytes are received in correct order (less strict) */
+#define CHECK_SEGMENTS_ON_OOSEQ 1
+
+#if CHECK_SEGMENTS_ON_OOSEQ
+#define EXPECT_OOSEQ(x) EXPECT(x)
+#else
+#define EXPECT_OOSEQ(x)
+#endif
+
+/* helper functions */
+
+/** Get the numbers of segments on the ooseq list */
+static int tcp_oos_count(struct tcp_pcb* pcb)
+{
+ int num = 0;
+ struct tcp_seg* seg = pcb->ooseq;
+ while(seg != NULL) {
+ num++;
+ seg = seg->next;
+ }
+ return num;
+}
+
+/** Get the numbers of pbufs on the ooseq list */
+static int tcp_oos_pbuf_count(struct tcp_pcb* pcb)
+{
+ int num = 0;
+ struct tcp_seg* seg = pcb->ooseq;
+ while(seg != NULL) {
+ num += pbuf_clen(seg->p);
+ seg = seg->next;
+ }
+ return num;
+}
+
+/** Get the seqno of a segment (by index) on the ooseq list
+ *
+ * @param pcb the pcb to check for ooseq segments
+ * @param seg_index index of the segment on the ooseq list
+ * @return seqno of the segment
+ */
+static u32_t
+tcp_oos_seg_seqno(struct tcp_pcb* pcb, int seg_index)
+{
+ int num = 0;
+ struct tcp_seg* seg = pcb->ooseq;
+
+ /* then check the actual segment */
+ while(seg != NULL) {
+ if(num == seg_index) {
+ return seg->tcphdr->seqno;
+ }
+ num++;
+ seg = seg->next;
+ }
+ fail();
+ return 0;
+}
+
+/** Get the tcplen (datalen + SYN/FIN) of a segment (by index) on the ooseq list
+ *
+ * @param pcb the pcb to check for ooseq segments
+ * @param seg_index index of the segment on the ooseq list
+ * @return tcplen of the segment
+ */
+static int
+tcp_oos_seg_tcplen(struct tcp_pcb* pcb, int seg_index)
+{
+ int num = 0;
+ struct tcp_seg* seg = pcb->ooseq;
+
+ /* then check the actual segment */
+ while(seg != NULL) {
+ if(num == seg_index) {
+ return TCP_TCPLEN(seg);
+ }
+ num++;
+ seg = seg->next;
+ }
+ fail();
+ return -1;
+}
+
+/** Get the tcplen (datalen + SYN/FIN) of all segments on the ooseq list
+ *
+ * @param pcb the pcb to check for ooseq segments
+ * @return tcplen of all segment
+ */
+static int
+tcp_oos_tcplen(struct tcp_pcb* pcb)
+{
+ int len = 0;
+ struct tcp_seg* seg = pcb->ooseq;
+
+ /* then check the actual segment */
+ while(seg != NULL) {
+ len += TCP_TCPLEN(seg);
+ seg = seg->next;
+ }
+ return len;
+}
+
+/* Setup/teardown functions */
+
+static void
+tcp_oos_setup(void)
+{
+ tcp_remove_all();
+}
+
+static void
+tcp_oos_teardown(void)
+{
+ tcp_remove_all();
+}
+
+
+
+/* Test functions */
+
+/** create multiple segments and pass them to tcp_input in a wrong
+ * order to see if ooseq-caching works correctly
+ * FIN is received in out-of-sequence segments only */
+START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ)
+{
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *p_8_9, *p_4_8, *p_4_10, *p_2_14, *p_fin, *pinseq;
+ char data[] = {
+ 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16};
+ ip_addr_t remote_ip, local_ip;
+ u16_t data_len;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ data_len = sizeof(data);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = data_len;
+ counters.expected_data = data;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+
+ /* create segments */
+ /* pinseq is sent as last segment! */
+ pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK);
+ /* p1: 8 bytes before FIN */
+ /* seqno: 8..16 */
+ p_8_9 = tcp_create_rx_segment(pcb, &data[8], 8, 8, 0, TCP_ACK|TCP_FIN);
+ /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */
+ /* seqno: 4..11 */
+ p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK);
+ /* p3: same as p2 but 2 bytes longer */
+ /* seqno: 4..13 */
+ p_4_10 = tcp_create_rx_segment(pcb, &data[4], 10, 4, 0, TCP_ACK);
+ /* p4: 14 bytes before FIN, includes data from p1 and p2, plus partly from pinseq */
+ /* seqno: 2..15 */
+ p_2_14 = tcp_create_rx_segment(pcb, &data[2], 14, 2, 0, TCP_ACK);
+ /* FIN, seqno 16 */
+ p_fin = tcp_create_rx_segment(pcb, NULL, 0,16, 0, TCP_ACK|TCP_FIN);
+ EXPECT(pinseq != NULL);
+ EXPECT(p_8_9 != NULL);
+ EXPECT(p_4_8 != NULL);
+ EXPECT(p_4_10 != NULL);
+ EXPECT(p_2_14 != NULL);
+ EXPECT(p_fin != NULL);
+ if ((pinseq != NULL) && (p_8_9 != NULL) && (p_4_8 != NULL) && (p_4_10 != NULL) && (p_2_14 != NULL) && (p_fin != NULL)) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_8_9, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 8);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 9); /* includes FIN */
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_4_8, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_4_10, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* ooseq queue: unchanged */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_2_14, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_fin, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* ooseq queue: unchanged */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(pinseq, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 1);
+ EXPECT(counters.recv_calls == 1);
+ EXPECT(counters.recved_bytes == data_len);
+ EXPECT(counters.err_calls == 0);
+ EXPECT(pcb->ooseq == NULL);
+ }
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+END_TEST
+
+
+/** create multiple segments and pass them to tcp_input in a wrong
+ * order to see if ooseq-caching works correctly
+ * FIN is received IN-SEQUENCE at the end */
+START_TEST(test_tcp_recv_ooseq_FIN_INSEQ)
+{
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *p_1_2, *p_4_8, *p_3_11, *p_2_12, *p_15_1, *p_15_1a, *pinseq, *pinseqFIN;
+ char data[] = {
+ 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16};
+ ip_addr_t remote_ip, local_ip;
+ u16_t data_len;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ data_len = sizeof(data);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = data_len;
+ counters.expected_data = data;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+
+ /* create segments */
+ /* p1: 7 bytes - 2 before FIN */
+ /* seqno: 1..2 */
+ p_1_2 = tcp_create_rx_segment(pcb, &data[1], 2, 1, 0, TCP_ACK);
+ /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */
+ /* seqno: 4..11 */
+ p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK);
+ /* p3: same as p2 but 2 bytes longer and one byte more at the front */
+ /* seqno: 3..13 */
+ p_3_11 = tcp_create_rx_segment(pcb, &data[3], 11, 3, 0, TCP_ACK);
+ /* p4: 13 bytes - 2 before FIN - should be ignored as contained in p1 and p3 */
+ /* seqno: 2..13 */
+ p_2_12 = tcp_create_rx_segment(pcb, &data[2], 12, 2, 0, TCP_ACK);
+ /* pinseq is the first segment that is held back to create ooseq! */
+ /* seqno: 0..3 */
+ pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK);
+ /* p5: last byte before FIN */
+ /* seqno: 15 */
+ p_15_1 = tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK);
+ /* p6: same as p5, should be ignored */
+ p_15_1a= tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK);
+ /* pinseqFIN: last 2 bytes plus FIN */
+ /* only segment containing seqno 14 and FIN */
+ pinseqFIN = tcp_create_rx_segment(pcb, &data[14], 2, 14, 0, TCP_ACK|TCP_FIN);
+ EXPECT(pinseq != NULL);
+ EXPECT(p_1_2 != NULL);
+ EXPECT(p_4_8 != NULL);
+ EXPECT(p_3_11 != NULL);
+ EXPECT(p_2_12 != NULL);
+ EXPECT(p_15_1 != NULL);
+ EXPECT(p_15_1a != NULL);
+ EXPECT(pinseqFIN != NULL);
+ if ((pinseq != NULL) && (p_1_2 != NULL) && (p_4_8 != NULL) && (p_3_11 != NULL) && (p_2_12 != NULL)
+ && (p_15_1 != NULL) && (p_15_1a != NULL) && (pinseqFIN != NULL)) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_1_2, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_4_8, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 4);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 8);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_3_11, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2);
+ /* p_3_11 has removed p_4_8 from ooseq */
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 3);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 11);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_2_12, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 2);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 12);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(pinseq, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 1);
+ EXPECT(counters.recved_bytes == 14);
+ EXPECT(counters.err_calls == 0);
+ EXPECT(pcb->ooseq == NULL);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_15_1, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 1);
+ EXPECT(counters.recved_bytes == 14);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_15_1a, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 1);
+ EXPECT(counters.recved_bytes == 14);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue: unchanged */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == 1);
+ EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15);
+ EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1);
+
+ /* pass the segment to tcp_input */
+ test_tcp_input(pinseqFIN, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 1);
+ EXPECT(counters.recv_calls == 2);
+ EXPECT(counters.recved_bytes == data_len);
+ EXPECT(counters.err_calls == 0);
+ EXPECT(pcb->ooseq == NULL);
+ }
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+END_TEST
+
+static char data_full_wnd[TCP_WND];
+
+/** create multiple segments and pass them to tcp_input with the first segment missing
+ * to simulate overruning the rxwin with ooseq queueing enabled */
+START_TEST(test_tcp_recv_ooseq_overrun_rxwin)
+{
+#if !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS
+ int i, k;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *pinseq, *p_ovr;
+ ip_addr_t remote_ip, local_ip;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ int datalen = 0;
+ int datalen2;
+
+ for(i = 0; i < sizeof(data_full_wnd); i++) {
+ data_full_wnd[i] = (char)i;
+ }
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = TCP_WND;
+ counters.expected_data = data_full_wnd;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->rcv_nxt = 0x8000;
+
+ /* create segments */
+ /* pinseq is sent as last segment! */
+ pinseq = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK);
+
+ for(i = TCP_MSS, k = 0; i < TCP_WND; i += TCP_MSS, k++) {
+ int count, expected_datalen;
+ struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)],
+ TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ count = tcp_oos_count(pcb);
+ EXPECT_OOSEQ(count == k+1);
+ datalen = tcp_oos_tcplen(pcb);
+ if (i + TCP_MSS < TCP_WND) {
+ expected_datalen = (k+1)*TCP_MSS;
+ } else {
+ expected_datalen = TCP_WND - TCP_MSS;
+ }
+ if (datalen != expected_datalen) {
+ EXPECT_OOSEQ(datalen == expected_datalen);
+ }
+ }
+
+ /* pass in one more segment, cleary overrunning the rxwin */
+ p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK);
+ EXPECT_RET(p_ovr != NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_ovr, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == k);
+ datalen2 = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(datalen == datalen2);
+
+ /* now pass inseq */
+ test_tcp_input(pinseq, &netif);
+ EXPECT(pcb->ooseq == NULL);
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+#endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */
+ LWIP_UNUSED_ARG(_i);
+}
+END_TEST
+
+START_TEST(test_tcp_recv_ooseq_max_bytes)
+{
+#if TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+ int i, k;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *p_ovr;
+ ip_addr_t remote_ip, local_ip;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ int datalen = 0;
+ int datalen2;
+
+ for(i = 0; i < sizeof(data_full_wnd); i++) {
+ data_full_wnd[i] = (char)i;
+ }
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = TCP_WND;
+ counters.expected_data = data_full_wnd;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->rcv_nxt = 0x8000;
+
+ /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */
+
+ /* create segments and 'recv' them */
+ for(k = 1, i = 1; k < TCP_OOSEQ_MAX_BYTES; k += TCP_MSS, i++) {
+ int count;
+ struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[k],
+ TCP_MSS, k, 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ EXPECT_RET(p->next == NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ count = tcp_oos_pbuf_count(pcb);
+ EXPECT_OOSEQ(count == i);
+ datalen = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(datalen == (i * TCP_MSS));
+ }
+
+ /* pass in one more segment, overrunning the limit */
+ p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[k+1], 1, k+1, 0, TCP_ACK);
+ EXPECT_RET(p_ovr != NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_ovr, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue (ensure the new segment was not accepted) */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1));
+ datalen2 = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(datalen2 == ((i-1) * TCP_MSS));
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+#endif /* TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */
+ LWIP_UNUSED_ARG(_i);
+}
+END_TEST
+
+START_TEST(test_tcp_recv_ooseq_max_pbufs)
+{
+#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+ int i;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *p_ovr;
+ ip_addr_t remote_ip, local_ip;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ int datalen = 0;
+ int datalen2;
+
+ for(i = 0; i < sizeof(data_full_wnd); i++) {
+ data_full_wnd[i] = (char)i;
+ }
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = TCP_WND;
+ counters.expected_data = data_full_wnd;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->rcv_nxt = 0x8000;
+
+ /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */
+
+ /* create segments and 'recv' them */
+ for(i = 1; i <= TCP_OOSEQ_MAX_PBUFS; i++) {
+ int count;
+ struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[i],
+ 1, i, 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ EXPECT_RET(p->next == NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ count = tcp_oos_pbuf_count(pcb);
+ EXPECT_OOSEQ(count == i);
+ datalen = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(datalen == i);
+ }
+
+ /* pass in one more segment, overrunning the limit */
+ p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[i+1], 1, i+1, 0, TCP_ACK);
+ EXPECT_RET(p_ovr != NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_ovr, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue (ensure the new segment was not accepted) */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1));
+ datalen2 = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(datalen2 == (i-1));
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+#endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */
+ LWIP_UNUSED_ARG(_i);
+}
+END_TEST
+
+static void
+check_rx_counters(struct tcp_pcb *pcb, struct test_tcp_counters *counters, u32_t exp_close_calls, u32_t exp_rx_calls,
+ u32_t exp_rx_bytes, u32_t exp_err_calls, int exp_oos_count, int exp_oos_len)
+{
+ int oos_len;
+ EXPECT(counters->close_calls == exp_close_calls);
+ EXPECT(counters->recv_calls == exp_rx_calls);
+ EXPECT(counters->recved_bytes == exp_rx_bytes);
+ EXPECT(counters->err_calls == exp_err_calls);
+ /* check that pbuf is queued in ooseq */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == exp_oos_count);
+ oos_len = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(exp_oos_len == oos_len);
+}
+
+/* this test uses 4 packets:
+ * - data (len=TCP_MSS)
+ * - FIN
+ * - data after FIN (len=1) (invalid)
+ * - 2nd FIN (invalid)
+ *
+ * the parameter 'delay_packet' is a bitmask that choses which on these packets is ooseq
+ */
+static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
+{
+ int i, k;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *p_normal_fin, *p_data_after_fin, *p, *p_2nd_fin_ooseq;
+ ip_addr_t remote_ip, local_ip;
+ u16_t remote_port = 0x100, local_port = 0x101;
+ struct netif netif;
+ u32_t exp_rx_calls = 0, exp_rx_bytes = 0, exp_close_calls = 0, exp_oos_pbufs = 0, exp_oos_tcplen = 0;
+ int first_dropped = 0xff;
+ int last_dropped = 0;
+
+ for(i = 0; i < sizeof(data_full_wnd); i++) {
+ data_full_wnd[i] = (char)i;
+ }
+
+ /* initialize local vars */
+ memset(&netif, 0, sizeof(netif));
+ IP4_ADDR(&local_ip, 192, 168, 1, 1);
+ IP4_ADDR(&remote_ip, 192, 168, 1, 2);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = TCP_WND;
+ counters.expected_data = data_full_wnd;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ pcb->rcv_nxt = 0x8000;
+
+ /* create segments */
+ p = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK);
+ p_normal_fin = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS, 0, TCP_ACK|TCP_FIN);
+ k = 1;
+ p_data_after_fin = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS+1], k, TCP_MSS+1, 0, TCP_ACK);
+ p_2nd_fin_ooseq = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS+1+k, 0, TCP_ACK|TCP_FIN);
+
+ if(delay_packet & 1) {
+ /* drop normal data */
+ first_dropped = 1;
+ last_dropped = 1;
+ } else {
+ /* send normal data */
+ test_tcp_input(p, &netif);
+ exp_rx_calls++;
+ exp_rx_bytes += TCP_MSS;
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 2) {
+ /* drop FIN */
+ if(first_dropped > 2) {
+ first_dropped = 2;
+ }
+ last_dropped = 2;
+ } else {
+ /* send FIN */
+ test_tcp_input(p_normal_fin, &netif);
+ if (first_dropped < 2) {
+ /* already dropped packets, this one is ooseq */
+ exp_oos_pbufs++;
+ exp_oos_tcplen++;
+ } else {
+ /* inseq */
+ exp_close_calls++;
+ }
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 4) {
+ /* drop data-after-FIN */
+ if(first_dropped > 3) {
+ first_dropped = 3;
+ }
+ last_dropped = 3;
+ } else {
+ /* send data-after-FIN */
+ test_tcp_input(p_data_after_fin, &netif);
+ if (first_dropped < 3) {
+ /* already dropped packets, this one is ooseq */
+ if (delay_packet & 2) {
+ /* correct FIN was ooseq */
+ exp_oos_pbufs++;
+ exp_oos_tcplen += k;
+ }
+ } else {
+ /* inseq: no change */
+ }
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 8) {
+ /* drop 2nd-FIN */
+ if(first_dropped > 4) {
+ first_dropped = 4;
+ }
+ last_dropped = 4;
+ } else {
+ /* send 2nd-FIN */
+ test_tcp_input(p_2nd_fin_ooseq, &netif);
+ if (first_dropped < 3) {
+ /* already dropped packets, this one is ooseq */
+ if (delay_packet & 2) {
+ /* correct FIN was ooseq */
+ exp_oos_pbufs++;
+ exp_oos_tcplen++;
+ }
+ } else {
+ /* inseq: no change */
+ }
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 1) {
+ /* dropped normal data before */
+ test_tcp_input(p, &netif);
+ exp_rx_calls++;
+ exp_rx_bytes += TCP_MSS;
+ if((delay_packet & 2) == 0) {
+ /* normal FIN was NOT delayed */
+ exp_close_calls++;
+ exp_oos_pbufs = exp_oos_tcplen = 0;
+ }
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 2) {
+ /* dropped normal FIN before */
+ test_tcp_input(p_normal_fin, &netif);
+ exp_close_calls++;
+ exp_oos_pbufs = exp_oos_tcplen = 0;
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 4) {
+ /* dropped data-after-FIN before */
+ test_tcp_input(p_data_after_fin, &netif);
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ if(delay_packet & 8) {
+ /* dropped 2nd-FIN before */
+ test_tcp_input(p_2nd_fin_ooseq, &netif);
+ }
+ /* check if counters are as expected */
+ check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen);
+
+ /* check that ooseq data has been dumped */
+ EXPECT(pcb->ooseq == NULL);
+
+ /* make sure the pcb is freed */
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ tcp_abort(pcb);
+ EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+}
+
+/** create multiple segments and pass them to tcp_input with the first segment missing
+ * to simulate overruning the rxwin with ooseq queueing enabled */
+#define FIN_TEST(name, num) \
+ START_TEST(name) \
+ { \
+ LWIP_UNUSED_ARG(_i); \
+ test_tcp_recv_ooseq_double_FINs(num); \
+ } \
+ END_TEST
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_0, 0)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_1, 1)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_2, 2)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_3, 3)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_4, 4)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_5, 5)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_6, 6)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_7, 7)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_8, 8)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_9, 9)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_10, 10)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_11, 11)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_12, 12)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_13, 13)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_14, 14)
+FIN_TEST(test_tcp_recv_ooseq_double_FIN_15, 15)
+
+
+/** Create the suite including all tests for this module */
+Suite *
+tcp_oos_suite(void)
+{
+ TFun tests[] = {
+ test_tcp_recv_ooseq_FIN_OOSEQ,
+ test_tcp_recv_ooseq_FIN_INSEQ,
+ test_tcp_recv_ooseq_overrun_rxwin,
+ test_tcp_recv_ooseq_max_bytes,
+ test_tcp_recv_ooseq_max_pbufs,
+ test_tcp_recv_ooseq_double_FIN_0,
+ test_tcp_recv_ooseq_double_FIN_1,
+ test_tcp_recv_ooseq_double_FIN_2,
+ test_tcp_recv_ooseq_double_FIN_3,
+ test_tcp_recv_ooseq_double_FIN_4,
+ test_tcp_recv_ooseq_double_FIN_5,
+ test_tcp_recv_ooseq_double_FIN_6,
+ test_tcp_recv_ooseq_double_FIN_7,
+ test_tcp_recv_ooseq_double_FIN_8,
+ test_tcp_recv_ooseq_double_FIN_9,
+ test_tcp_recv_ooseq_double_FIN_10,
+ test_tcp_recv_ooseq_double_FIN_11,
+ test_tcp_recv_ooseq_double_FIN_12,
+ test_tcp_recv_ooseq_double_FIN_13,
+ test_tcp_recv_ooseq_double_FIN_14,
+ test_tcp_recv_ooseq_double_FIN_15
+ };
+ return create_suite("TCP_OOS", tests, sizeof(tests)/sizeof(TFun), tcp_oos_setup, tcp_oos_teardown);
+}
diff --git a/Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.h b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.h
new file mode 100644
index 00000000..5e411f00
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/tcp/test_tcp_oos.h
@@ -0,0 +1,8 @@
+#ifndef __TEST_TCP_OOS_H__
+#define __TEST_TCP_OOS_H__
+
+#include "../lwip_check.h"
+
+Suite *tcp_oos_suite(void);
+
+#endif
diff --git a/Libraries/Lwip/lwip/test/unit/udp/test_udp.c b/Libraries/Lwip/lwip/test/unit/udp/test_udp.c
new file mode 100644
index 00000000..a2f02af0
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/udp/test_udp.c
@@ -0,0 +1,68 @@
+#include "test_udp.h"
+
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS
+#error "This tests needs UDP- and MEMP-statistics enabled"
+#endif
+
+/* Helper functions */
+static void
+udp_remove_all(void)
+{
+ struct udp_pcb *pcb = udp_pcbs;
+ struct udp_pcb *pcb2;
+
+ while(pcb != NULL) {
+ pcb2 = pcb;
+ pcb = pcb->next;
+ udp_remove(pcb2);
+ }
+ fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0);
+}
+
+/* Setups/teardown functions */
+
+static void
+udp_setup(void)
+{
+ udp_remove_all();
+}
+
+static void
+udp_teardown(void)
+{
+ udp_remove_all();
+}
+
+
+/* Test functions */
+
+START_TEST(test_udp_new_remove)
+{
+ struct udp_pcb* pcb;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0);
+
+ pcb = udp_new();
+ fail_unless(pcb != NULL);
+ if (pcb != NULL) {
+ fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 1);
+ udp_remove(pcb);
+ fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0);
+ }
+}
+END_TEST
+
+
+/** Create the suite including all tests for this module */
+Suite *
+udp_suite(void)
+{
+ TFun tests[] = {
+ test_udp_new_remove,
+ };
+ return create_suite("UDP", tests, sizeof(tests)/sizeof(TFun), udp_setup, udp_teardown);
+}
diff --git a/Libraries/Lwip/lwip/test/unit/udp/test_udp.h b/Libraries/Lwip/lwip/test/unit/udp/test_udp.h
new file mode 100644
index 00000000..93353682
--- /dev/null
+++ b/Libraries/Lwip/lwip/test/unit/udp/test_udp.h
@@ -0,0 +1,8 @@
+#ifndef __TEST_UDP_H__
+#define __TEST_UDP_H__
+
+#include "../lwip_check.h"
+
+Suite* udp_suite(void);
+
+#endif
diff --git a/Libraries/Lwip/lwipopts.h b/Libraries/Lwip/lwipopts.h
index 72dbceb2..a4057409 100644
--- a/Libraries/Lwip/lwipopts.h
+++ b/Libraries/Lwip/lwipopts.h
@@ -47,22 +47,22 @@
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
-/* Uncomment the following line to enable LWIP debugging */
-//#define LWIP_DEBUG
-
/* Include user defined options first */
#include "conf_eth.h"
+#include "WMath.h"
+
+/* Uncomment the following line to enable LWIP debugging */
+//#define LWIP_DEBUG
/* ---------- System options ---------- */
/* Specify NO_SYS because we are not using an RTOS */
#define NO_SYS 1
#define LWIP_RAW 0
-#define LWIP_NETIF_STATUS_CALLBACK 1
-/* Enable DHCP */
-#define DHCP_USED 1
+/* Network interface options */
#define LWIP_NETIF_HOSTNAME 1
+#define LWIP_NETIF_STATUS_CALLBACK 1
/* These are not available when using "NO_SYS" */
#define LWIP_NETCONN 0
@@ -73,196 +73,240 @@
/* These two control is reclaimer functions should be compiled
in. Should always be turned on (1). */
-#define MEM_RECLAIM 1
-#define MEMP_RECLAIM 1
+#define MEM_RECLAIM 1
+#define MEMP_RECLAIM 1
/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
byte alignment -> define MEM_ALIGNMENT to 2. */
-#define MEM_ALIGNMENT (4)
+#define MEM_ALIGNMENT 4
/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
-#define MEM_SIZE (1024)
+#define MEM_SIZE 1024
/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
sends a lot of data out of ROM (or other static memory), this
should be set high. */
-#define MEMP_NUM_PBUF 8
+#define MEMP_NUM_PBUF 4
/* Number of raw connection PCBs */
-#define MEMP_NUM_RAW_PCB 0
+#define MEMP_NUM_RAW_PCB 0
+
+
+/* --------- IGMP options ---------- */
+#define LWIP_IGMP 1
+#define LWIP_RAND trueRandom
+#define SO_REUSE 1
+
+
+/* ---------- DNS options ---------- */
+#define LWIP_DNS 1
+#define DNS_MAX_NAME_LENGTH 64
+#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 1
+
/* ---------- UDP options ---------- */
-#define LWIP_UDP 1
-#define UDP_TTL 255
+#define LWIP_UDP 1
+#define UDP_TTL 255
+
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
per active UDP "connection". */
-#define MEMP_NUM_UDP_PCB 2
+#define MEMP_NUM_UDP_PCB 5
/* MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections. */
-#define MEMP_NUM_TCP_PCB 16
+#define MEMP_NUM_TCP_PCB 16
+
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. */
-#define MEMP_NUM_TCP_PCB_LISTEN 4
+#define MEMP_NUM_TCP_PCB_LISTEN 4
+
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. */
-#define MEMP_NUM_TCP_SEG 16
+#define MEMP_NUM_TCP_SEG 16
+
+/* MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for
+ * reassembly (whole packets, not fragments!) */
+#define MEMP_NUM_REASSDATA 2
+
+/* MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent
+ * (fragments, not whole packets!). */
+#define MEMP_NUM_FRAG_PBUF 8
+
/* MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts. */
-#define MEMP_NUM_SYS_TIMEOUT 8
+#define MEMP_NUM_SYS_TIMEOUT 8
/* The following four are used only with the sequential API and can be
set to 0 if the application only will use the raw API. */
/* MEMP_NUM_NETBUF: the number of struct netbufs. */
-#define MEMP_NUM_NETBUF 0
+#define MEMP_NUM_NETBUF 0
+
/* MEMP_NUM_NETCONN: the number of struct netconns. */
-#define MEMP_NUM_NETCONN 0
+#define MEMP_NUM_NETCONN 0
+
/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
-#define PBUF_POOL_SIZE (6)
-/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. DO NOT DEFINE IT HERE, the correct value needs to be calculated from TCP_MSS. */
+#define PBUF_POOL_SIZE 5
+
+/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool.
+ * DO NOT DEFINE IT HERE, the correct value needs to be calculated from TCP_MSS. */
+
+/**
+ * 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 3
+
+/**
+ * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast
+ * filter per pcb on udp and raw send operations. To enable broadcast filter
+ * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1.
+ */
+#define IP_SOF_BROADCAST 1
/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure
* alignment of payload after that header. Since the header is 14 bytes long,
* without this padding e.g. addresses in the IP header will not be aligned
* on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms.
*/
-#define ETH_PAD_SIZE (2)
+#define ETH_PAD_SIZE 2
/* PBUF_LINK_HLEN: the number of bytes that should be allocated for a
link level header. */
-#define PBUF_LINK_HLEN (16 + ETH_PAD_SIZE)
+#define PBUF_LINK_HLEN (16 + ETH_PAD_SIZE)
/* ---------- TCP options ---------- */
-#define LWIP_TCP (1)
-#define TCP_TTL (255)
-/* TCP receive window. */
-#define TCP_WND (2 * 1460)
+#define LWIP_TCP 1
+#define TCP_TTL 255
+
/* Controls if TCP should queue segments that arrive out of
order. Define to 0 if your device is low on memory. */
-#define TCP_QUEUE_OOSEQ 1
+#define TCP_QUEUE_OOSEQ 1
+
/* TCP Maximum segment size. */
-#define TCP_MSS (1460) // 1432 is optimal for Windows clients
+#define TCP_MSS 1460
+
+/* TCP receive window. */
+#define TCP_WND (2 * TCP_MSS)
+
/* TCP sender buffer space (bytes). */
-#define TCP_SND_BUF (2 * 1460) //changed from 2150 to pass LWIP sanity checks
+#define TCP_SND_BUF (2 * TCP_MSS)
+
/* TCP sender buffer space (pbufs). This must be at least = 2 * TCP_SND_BUF/TCP_MSS for things to work. */
-#define TCP_SND_QUEUELEN (3 * TCP_SND_BUF / TCP_MSS)
+#define TCP_SND_QUEUELEN (3 * TCP_SND_BUF / TCP_MSS)
+
/* Maximum number of retransmissions of data segments. */
-// #define TCP_MAXRTX 12
+#define TCP_MAXRTX 8
+
/* Maximum number of retransmissions of SYN segments. */
-// #define TCP_SYNMAXRTX 4
+#define TCP_SYNMAXRTX 4
+
+/* How frequently are the timer tasks called (in ms)? */
+#define TCP_TMR_INTERVAL 125
/* ---------- ARP options ---------- */
-#define ARP_TABLE_SIZE 10
-#define ARP_QUEUEING 0
+#define ARP_TABLE_SIZE 16
+#define ARP_QUEUEING 0
/* ---------- IP options ---------- */
/* Define IP_FORWARD to 1 if you wish to have the ability to forward
IP packets across network interfaces. If you are going to run lwIP
on a device with only one network interface, define this to 0. */
-#define IP_FORWARD 0
+#define IP_FORWARD 0
+
/* If defined to 1, IP options are allowed (but not parsed). If
defined to 0, all packets with IP options are dropped. */
-#define IP_OPTIONS 1
+#define IP_OPTIONS 1
/* ---------- ICMP options ---------- */
-#define ICMP_TTL (255)
+#define ICMP_TTL 255
-#if defined(DHCP_USED)
/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
turning this on does currently not work. */
-#define LWIP_DHCP 1
+#define LWIP_DHCP 1
/* 1 if you want to do an ARP check on the offered address
(recommended). */
-#define DHCP_DOES_ARP_CHECK 1
-#endif
+#define DHCP_DOES_ARP_CHECK 1
+
/* ---------- Statistics options ---------- */
-#define LWIP_STATS 1
-#define LWIP_STATS_DISPLAY 1
-
-#if LWIP_STATS
-#define LINK_STATS 1
-#define IP_STATS 1
-#define ICMP_STATS 1
-#define UDP_STATS 1
-#define TCP_STATS 1
-#define MEM_STATS 1
-#define MEMP_STATS 1
-#define PBUF_STATS 1
-#define SYS_STATS 1
-#endif /* STATS */
+#define LWIP_STATS 1
+#define LWIP_STATS_DISPLAY 1
/* ---------- Lwip Debug options ---------- */
/* Define default values for unconfigured parameters. */
// 1 == To suppress some errors for now (no debug output)
-#define LWIP_NOASSERT 0
+#define LWIP_NOASSERT 0
#ifdef LWIP_DEBUG
-#define ETHARP_DEBUG LWIP_DBG_ON
-#define NETIF_DEBUG LWIP_DBG_ON
-#define PBUF_DEBUG LWIP_DBG_ON
-#define API_LIB_DEBUG LWIP_DBG_ON
-#define API_MSG_DEBUG LWIP_DBG_ON
-#define SOCKETS_DEBUG LWIP_DBG_ON
-#define ICMP_DEBUG LWIP_DBG_ON
-#define INET_DEBUG LWIP_DBG_ON
-#define IP_DEBUG LWIP_DBG_ON
-#define IP_REASS_DEBUG LWIP_DBG_ON
-#define RAW_DEBUG LWIP_DBG_ON
-#define MEM_DEBUG LWIP_DBG_ON
-#define MEMP_DEBUG LWIP_DBG_ON
-#define SYS_DEBUG LWIP_DBG_ON
-#define TCP_DEBUG LWIP_DBG_ON
-#define TCP_INPUT_DEBUG LWIP_DBG_ON
-#define TCP_FR_DEBUG LWIP_DBG_ON
-#define TCP_RTO_DEBUG LWIP_DBG_ON
-#define TCP_CWND_DEBUG LWIP_DBG_ON
-#define TCP_WND_DEBUG LWIP_DBG_ON
-#define TCP_OUTPUT_DEBUG LWIP_DBG_ON
-#define TCP_RST_DEBUG LWIP_DBG_ON
-#define TCP_QLEN_DEBUG LWIP_DBG_ON
-#define UDP_DEBUG LWIP_DBG_ON
-#define TCPIP_DEBUG LWIP_DBG_ON
-#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_WARNING
+#define ETHARP_DEBUG LWIP_DBG_ON
+#define NETIF_DEBUG LWIP_DBG_ON
+#define PBUF_DEBUG LWIP_DBG_ON
+#define API_LIB_DEBUG LWIP_DBG_ON
+#define API_MSG_DEBUG LWIP_DBG_ON
+#define SOCKETS_DEBUG LWIP_DBG_ON
+#define ICMP_DEBUG LWIP_DBG_ON
+#define INET_DEBUG LWIP_DBG_ON
+#define IP_DEBUG LWIP_DBG_ON
+#define IP_REASS_DEBUG LWIP_DBG_ON
+#define RAW_DEBUG LWIP_DBG_ON
+#define MEM_DEBUG LWIP_DBG_ON
+#define MEMP_DEBUG LWIP_DBG_ON
+#define SYS_DEBUG LWIP_DBG_ON
+#define TCP_DEBUG LWIP_DBG_ON
+#define TCP_INPUT_DEBUG LWIP_DBG_ON
+#define TCP_FR_DEBUG LWIP_DBG_ON
+#define TCP_RTO_DEBUG LWIP_DBG_ON
+#define TCP_CWND_DEBUG LWIP_DBG_ON
+#define TCP_WND_DEBUG LWIP_DBG_ON
+#define TCP_OUTPUT_DEBUG LWIP_DBG_ON
+#define TCP_RST_DEBUG LWIP_DBG_ON
+#define TCP_QLEN_DEBUG LWIP_DBG_ON
+#define UDP_DEBUG LWIP_DBG_ON
+#define TCPIP_DEBUG LWIP_DBG_ON
+#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
#else
-#define ETHARP_DEBUG LWIP_DBG_ON
-#define NETIF_DEBUG LWIP_DBG_ON
-#define PBUF_DEBUG LWIP_DBG_ON
-#define API_LIB_DEBUG LWIP_DBG_ON
-#define API_MSG_DEBUG LWIP_DBG_ON
-#define SOCKETS_DEBUG LWIP_DBG_ON
-#define ICMP_DEBUG LWIP_DBG_ON
-#define INET_DEBUG LWIP_DBG_ON
-#define IP_DEBUG LWIP_DBG_ON
-#define IP_REASS_DEBUG LWIP_DBG_OFF
-#define RAW_DEBUG LWIP_DBG_OFF
-#define MEM_DEBUG LWIP_DBG_OFF
-#define MEMP_DEBUG LWIP_DBG_OFF
-#define SYS_DEBUG LWIP_DBG_ON
-#define TCP_DEBUG LWIP_DBG_OFF
-#define TCP_INPUT_DEBUG LWIP_DBG_OFF
-#define TCP_FR_DEBUG LWIP_DBG_OFF
-#define TCP_RTO_DEBUG LWIP_DBG_OFF
-#define TCP_CWND_DEBUG LWIP_DBG_OFF
-#define TCP_WND_DEBUG LWIP_DBG_OFF
-#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
-#define TCP_RST_DEBUG LWIP_DBG_OFF
-#define TCP_QLEN_DEBUG LWIP_DBG_OFF
-#define UDP_DEBUG LWIP_DBG_OFF
-#define TCPIP_DEBUG LWIP_DBG_OFF
-#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_SERIOUS
+#define ETHARP_DEBUG LWIP_DBG_ON
+#define NETIF_DEBUG LWIP_DBG_ON
+#define PBUF_DEBUG LWIP_DBG_ON
+#define API_LIB_DEBUG LWIP_DBG_ON
+#define API_MSG_DEBUG LWIP_DBG_ON
+#define SOCKETS_DEBUG LWIP_DBG_ON
+#define ICMP_DEBUG LWIP_DBG_ON
+#define INET_DEBUG LWIP_DBG_ON
+#define IP_DEBUG LWIP_DBG_ON
+#define IP_REASS_DEBUG LWIP_DBG_OFF
+#define RAW_DEBUG LWIP_DBG_OFF
+#define MEM_DEBUG LWIP_DBG_OFF
+#define MEMP_DEBUG LWIP_DBG_OFF
+#define SYS_DEBUG LWIP_DBG_ON
+#define TCP_DEBUG LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+#define TCP_FR_DEBUG LWIP_DBG_OFF
+#define TCP_RTO_DEBUG LWIP_DBG_OFF
+#define TCP_CWND_DEBUG LWIP_DBG_OFF
+#define TCP_WND_DEBUG LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
+#define TCP_RST_DEBUG LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+#define UDP_DEBUG LWIP_DBG_OFF
+#define TCPIP_DEBUG LWIP_DBG_OFF
+#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_SERIOUS
#endif
// \note For a list of all possible lwIP configurations, check http://lwip.wikia.com/wiki/Lwipopts.h
#endif /* __LWIPOPTS_H__ */
+
+// vim: ts=4:sw=4
diff --git a/Release/RepRapFirmware-1.09x-beta3.bin b/Release/RepRapFirmware-1.09x-beta3.bin
new file mode 100644
index 00000000..43e2fac4
--- /dev/null
+++ b/Release/RepRapFirmware-1.09x-beta3.bin
Binary files differ
diff --git a/SD-image/sys-CoreXY/iap.bin b/SD-image/sys-CoreXY/iap.bin
new file mode 100644
index 00000000..f7949880
--- /dev/null
+++ b/SD-image/sys-CoreXY/iap.bin
Binary files differ
diff --git a/SD-image/sys-Fisher/iap.bin b/SD-image/sys-Fisher/iap.bin
new file mode 100644
index 00000000..f7949880
--- /dev/null
+++ b/SD-image/sys-Fisher/iap.bin
Binary files differ
diff --git a/SD-image/sys-Huxley/iap.bin b/SD-image/sys-Huxley/iap.bin
new file mode 100644
index 00000000..f7949880
--- /dev/null
+++ b/SD-image/sys-Huxley/iap.bin
Binary files differ
diff --git a/SD-image/sys-MiniKossel/iap.bin b/SD-image/sys-MiniKossel/iap.bin
new file mode 100644
index 00000000..f7949880
--- /dev/null
+++ b/SD-image/sys-MiniKossel/iap.bin
Binary files differ
diff --git a/SD-image/sys-Ormerod1/iap.bin b/SD-image/sys-Ormerod1/iap.bin
new file mode 100644
index 00000000..f7949880
--- /dev/null
+++ b/SD-image/sys-Ormerod1/iap.bin
Binary files differ
diff --git a/SD-image/sys-Ormerod2/iap.bin b/SD-image/sys-Ormerod2/iap.bin
new file mode 100644
index 00000000..f7949880
--- /dev/null
+++ b/SD-image/sys-Ormerod2/iap.bin
Binary files differ
diff --git a/SD-image/www/.gitattributes b/SD-image/www/.gitattributes
deleted file mode 100644
index 412eeda7..00000000
--- a/SD-image/www/.gitattributes
+++ /dev/null
@@ -1,22 +0,0 @@
-# Auto detect text files and perform LF normalization
-* text=auto
-
-# Custom for Visual Studio
-*.cs diff=csharp
-*.sln merge=union
-*.csproj merge=union
-*.vbproj merge=union
-*.fsproj merge=union
-*.dbproj merge=union
-
-# Standard to msysgit
-*.doc diff=astextplain
-*.DOC diff=astextplain
-*.docx diff=astextplain
-*.DOCX diff=astextplain
-*.dot diff=astextplain
-*.DOT diff=astextplain
-*.pdf diff=astextplain
-*.PDF diff=astextplain
-*.rtf diff=astextplain
-*.RTF diff=astextplain
diff --git a/SD-image/www/css/defaults.css b/SD-image/www/css/defaults.css
index 687547c3..ba4e3458 100644
--- a/SD-image/www/css/defaults.css
+++ b/SD-image/www/css/defaults.css
@@ -978,6 +978,7 @@ label[for="dropdown_language"] {
#text_config {
background-color: inherit;
border: 0px;
+ margin-bottom: 15px;
min-height: 200px;
resize: none;
width: 100%;
diff --git a/SD-image/www/js/jquery.autosize.js b/SD-image/www/js/jquery.autosize.js
deleted file mode 100644
index 4a7bc8fd..00000000
--- a/SD-image/www/js/jquery.autosize.js
+++ /dev/null
@@ -1,274 +0,0 @@
-/*!
- Autosize 1.18.13
- license: MIT
- http://www.jacklmoore.com/autosize
-*/
-(function ($) {
- var
- defaults = {
- className: 'autosizejs',
- id: 'autosizejs',
- append: '\n',
- callback: false,
- resizeDelay: 10,
- placeholder: true
- },
-
- // border:0 is unnecessary, but avoids a bug in Firefox on OSX
- copy = '<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',
-
- // line-height is conditionally included because IE7/IE8/old Opera do not return the correct value.
- typographyStyles = [
- 'fontFamily',
- 'fontSize',
- 'fontWeight',
- 'fontStyle',
- 'letterSpacing',
- 'textTransform',
- 'wordSpacing',
- 'textIndent',
- 'whiteSpace'
- ],
-
- // to keep track which textarea is being mirrored when adjust() is called.
- mirrored,
-
- // the mirror element, which is used to calculate what size the mirrored element should be.
- mirror = $(copy).data('autosize', true)[0];
-
- // test that line-height can be accurately copied.
- mirror.style.lineHeight = '99px';
- if ($(mirror).css('lineHeight') === '99px') {
- typographyStyles.push('lineHeight');
- }
- mirror.style.lineHeight = '';
-
- $.fn.autosize = function (options) {
- if (!this.length) {
- return this;
- }
-
- options = $.extend({}, defaults, options || {});
-
- if (mirror.parentNode !== document.body) {
- $(document.body).append(mirror);
- }
-
- return this.each(function () {
- var
- ta = this,
- $ta = $(ta),
- maxHeight,
- minHeight,
- boxOffset = 0,
- callback = $.isFunction(options.callback),
- originalStyles = {
- height: ta.style.height,
- overflow: ta.style.overflow,
- overflowY: ta.style.overflowY,
- wordWrap: ta.style.wordWrap,
- resize: ta.style.resize
- },
- timeout,
- width = $ta.width(),
- taResize = $ta.css('resize');
-
- if ($ta.data('autosize')) {
- // exit if autosize has already been applied, or if the textarea is the mirror element.
- return;
- }
- $ta.data('autosize', true);
-
- if ($ta.css('box-sizing') === 'border-box' || $ta.css('-moz-box-sizing') === 'border-box' || $ta.css('-webkit-box-sizing') === 'border-box'){
- boxOffset = $ta.outerHeight() - $ta.height();
- }
-
- // IE8 and lower return 'auto', which parses to NaN, if no min-height is set.
- minHeight = Math.max(parseInt($ta.css('minHeight'), 10) - boxOffset || 0, $ta.height());
-
- $ta.css({
- overflow: 'hidden',
- overflowY: 'hidden',
- wordWrap: 'break-word' // horizontal overflow is hidden, so break-word is necessary for handling words longer than the textarea width
- });
-
- if (taResize === 'vertical') {
- $ta.css('resize','none');
- } else if (taResize === 'both') {
- $ta.css('resize', 'horizontal');
- }
-
- // The mirror width must exactly match the textarea width, so using getBoundingClientRect because it doesn't round the sub-pixel value.
- // window.getComputedStyle, getBoundingClientRect returning a width are unsupported, but also unneeded in IE8 and lower.
- function setWidth() {
- var width;
- var style = window.getComputedStyle ? window.getComputedStyle(ta, null) : false;
-
- if (style) {
-
- width = ta.getBoundingClientRect().width;
-
- if (width === 0 || typeof width !== 'number') {
- width = parseInt(style.width,10);
- }
-
- $.each(['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'], function(i,val){
- width -= parseInt(style[val],10);
- });
- } else {
- width = $ta.width();
- }
-
- mirror.style.width = Math.max(width,0) + 'px';
- }
-
- function initMirror() {
- var styles = {};
-
- mirrored = ta;
- mirror.className = options.className;
- mirror.id = options.id;
- maxHeight = parseInt($ta.css('maxHeight'), 10);
-
- // mirror is a duplicate textarea located off-screen that
- // is automatically updated to contain the same text as the
- // original textarea. mirror always has a height of 0.
- // This gives a cross-browser supported way getting the actual
- // height of the text, through the scrollTop property.
- $.each(typographyStyles, function(i,val){
- styles[val] = $ta.css(val);
- });
-
- $(mirror).css(styles).attr('wrap', $ta.attr('wrap'));
-
- setWidth();
-
- // Chrome-specific fix:
- // When the textarea y-overflow is hidden, Chrome doesn't reflow the text to account for the space
- // made available by removing the scrollbar. This workaround triggers the reflow for Chrome.
- if (window.chrome) {
- var width = ta.style.width;
- ta.style.width = '0px';
- var ignore = ta.offsetWidth;
- ta.style.width = width;
- }
- }
-
- // Using mainly bare JS in this function because it is going
- // to fire very often while typing, and needs to very efficient.
- function adjust() {
- var height, original;
-
- if (mirrored !== ta) {
- initMirror();
- } else {
- setWidth();
- }
-
- if (!ta.value && options.placeholder) {
- // If the textarea is empty, copy the placeholder text into
- // the mirror control and use that for sizing so that we
- // don't end up with placeholder getting trimmed.
- mirror.value = ($ta.attr("placeholder") || '');
- } else {
- mirror.value = ta.value;
- }
-
- mirror.value += options.append || '';
- mirror.style.overflowY = ta.style.overflowY;
- original = parseInt(ta.style.height,10);
-
- // Setting scrollTop to zero is needed in IE8 and lower for the next step to be accurately applied
- mirror.scrollTop = 0;
-
- mirror.scrollTop = 9e4;
-
- // Using scrollTop rather than scrollHeight because scrollHeight is non-standard and includes padding.
- height = mirror.scrollTop;
-
- if (maxHeight && height > maxHeight) {
- ta.style.overflowY = 'scroll';
- height = maxHeight;
- } else {
- ta.style.overflowY = 'hidden';
- if (height < minHeight) {
- height = minHeight;
- }
- }
-
- height += boxOffset;
-
- if (original !== height) {
- ta.style.height = height + 'px';
- if (callback) {
- options.callback.call(ta,ta);
- }
- $ta.trigger('autosize.resized');
- }
- }
-
- function resize () {
- clearTimeout(timeout);
- timeout = setTimeout(function(){
- var newWidth = $ta.width();
-
- if (newWidth !== width) {
- width = newWidth;
- adjust();
- }
- }, parseInt(options.resizeDelay,10));
- }
-
- if ('onpropertychange' in ta) {
- if ('oninput' in ta) {
- // Detects IE9. IE9 does not fire onpropertychange or oninput for deletions,
- // so binding to onkeyup to catch most of those occasions. There is no way that I
- // know of to detect something like 'cut' in IE9.
- $ta.on('input.autosize keyup.autosize', adjust);
- } else {
- // IE7 / IE8
- $ta.on('propertychange.autosize', function(){
- if(event.propertyName === 'value'){
- adjust();
- }
- });
- }
- } else {
- // Modern Browsers
- $ta.on('input.autosize', adjust);
- }
-
- // Set options.resizeDelay to false if using fixed-width textarea elements.
- // Uses a timeout and width check to reduce the amount of times adjust needs to be called after window resize.
-
- if (options.resizeDelay !== false) {
- $(window).on('resize.autosize', resize);
- }
-
- // Event for manual triggering if needed.
- // Should only be needed when the value of the textarea is changed through JavaScript rather than user input.
- $ta.on('autosize.resize', adjust);
-
- // Event for manual triggering that also forces the styles to update as well.
- // Should only be needed if one of typography styles of the textarea change, and the textarea is already the target of the adjust method.
- $ta.on('autosize.resizeIncludeStyle', function() {
- mirrored = null;
- adjust();
- });
-
- $ta.on('autosize.destroy', function(){
- mirrored = null;
- clearTimeout(timeout);
- $(window).off('resize', resize);
- $ta
- .off('autosize')
- .off('.autosize')
- .css(originalStyles)
- .removeData('autosize');
- });
-
- // Call adjust in case the textarea already contains text.
- adjust();
- });
- };
-}(jQuery || $)); // jQuery or jQuery-like library, such as Zepto
diff --git a/SD-image/www/js/jquery.autosize.min.js b/SD-image/www/js/jquery.autosize.min.js
deleted file mode 100644
index 9a29f696..00000000
--- a/SD-image/www/js/jquery.autosize.min.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*!
- Autosize 1.18.13
- license: MIT
- http://www.jacklmoore.com/autosize
-*/
-(function(e){var t,o={className:"autosizejs",id:"autosizejs",append:"\n",callback:!1,resizeDelay:10,placeholder:!0},i='<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',n=["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent","whiteSpace"],s=e(i).data("autosize",!0)[0];s.style.lineHeight="99px","99px"===e(s).css("lineHeight")&&n.push("lineHeight"),s.style.lineHeight="",e.fn.autosize=function(i){return this.length?(i=e.extend({},o,i||{}),s.parentNode!==document.body&&e(document.body).append(s),this.each(function(){function o(){var t,o=window.getComputedStyle?window.getComputedStyle(u,null):!1;o?(t=u.getBoundingClientRect().width,(0===t||"number"!=typeof t)&&(t=parseInt(o.width,10)),e.each(["paddingLeft","paddingRight","borderLeftWidth","borderRightWidth"],function(e,i){t-=parseInt(o[i],10)})):t=p.width(),s.style.width=Math.max(t,0)+"px"}function a(){var a={};if(t=u,s.className=i.className,s.id=i.id,d=parseInt(p.css("maxHeight"),10),e.each(n,function(e,t){a[t]=p.css(t)}),e(s).css(a).attr("wrap",p.attr("wrap")),o(),window.chrome){var r=u.style.width;u.style.width="0px",u.offsetWidth,u.style.width=r}}function r(){var e,n;t!==u?a():o(),s.value=!u.value&&i.placeholder?p.attr("placeholder")||"":u.value,s.value+=i.append||"",s.style.overflowY=u.style.overflowY,n=parseInt(u.style.height,10),s.scrollTop=0,s.scrollTop=9e4,e=s.scrollTop,d&&e>d?(u.style.overflowY="scroll",e=d):(u.style.overflowY="hidden",c>e&&(e=c)),e+=w,n!==e&&(u.style.height=e+"px",f&&i.callback.call(u,u),p.trigger("autosize.resized"))}function l(){clearTimeout(h),h=setTimeout(function(){var e=p.width();e!==g&&(g=e,r())},parseInt(i.resizeDelay,10))}var d,c,h,u=this,p=e(u),w=0,f=e.isFunction(i.callback),z={height:u.style.height,overflow:u.style.overflow,overflowY:u.style.overflowY,wordWrap:u.style.wordWrap,resize:u.style.resize},g=p.width(),y=p.css("resize");p.data("autosize")||(p.data("autosize",!0),("border-box"===p.css("box-sizing")||"border-box"===p.css("-moz-box-sizing")||"border-box"===p.css("-webkit-box-sizing"))&&(w=p.outerHeight()-p.height()),c=Math.max(parseInt(p.css("minHeight"),10)-w||0,p.height()),p.css({overflow:"hidden",overflowY:"hidden",wordWrap:"break-word"}),"vertical"===y?p.css("resize","none"):"both"===y&&p.css("resize","horizontal"),"onpropertychange"in u?"oninput"in u?p.on("input.autosize keyup.autosize",r):p.on("propertychange.autosize",function(){"value"===event.propertyName&&r()}):p.on("input.autosize",r),i.resizeDelay!==!1&&e(window).on("resize.autosize",l),p.on("autosize.resize",r),p.on("autosize.resizeIncludeStyle",function(){t=null,r()}),p.on("autosize.destroy",function(){t=null,clearTimeout(h),e(window).off("resize",l),p.off("autosize").off(".autosize").css(z).removeData("autosize")}),r())})):this}})(jQuery||$); \ No newline at end of file
diff --git a/SD-image/www/js/jquery.textarea_autosize.js b/SD-image/www/js/jquery.textarea_autosize.js
new file mode 100644
index 00000000..2801b379
--- /dev/null
+++ b/SD-image/www/js/jquery.textarea_autosize.js
@@ -0,0 +1,54 @@
+/*!
+ * jQuery Textarea AutoSize plugin
+ * Author: Javier Julio
+ * Licensed under the MIT license
+ */
+;(function ($, window, document, undefined) {
+
+ var pluginName = "textareaAutoSize";
+ var pluginDataName = "plugin_" + pluginName;
+
+ var containsText = function (value) {
+ return (value.replace(/\s/g, '').length > 0);
+ };
+
+ function Plugin(element, options) {
+ this.element = element;
+ this.$element = $(element);
+ this.init();
+ }
+
+ Plugin.prototype = {
+ init: function() {
+ var height = this.$element.outerHeight();
+ var diff = parseInt(this.$element.css('paddingBottom')) +
+ parseInt(this.$element.css('paddingTop')) || 0;
+
+ if (containsText(this.element.value)) {
+ this.$element.height(this.element.scrollHeight - diff);
+ }
+
+ // keyup is required for IE to properly reset height when deleting text
+ this.$element.on('input keyup', function(event) {
+ var $window = $(window);
+ var currentScrollPosition = $window.scrollTop();
+
+ $(this)
+ .height(0)
+ .height(this.scrollHeight - diff);
+
+ $window.scrollTop(currentScrollPosition);
+ });
+ }
+ };
+
+ $.fn[pluginName] = function (options) {
+ this.each(function() {
+ if (!$.data(this, pluginDataName)) {
+ $.data(this, pluginDataName, new Plugin(this, options));
+ }
+ });
+ return this;
+ };
+
+})(jQuery, window, document);
diff --git a/SD-image/www/js/jquery.textarea_autosize.min.js b/SD-image/www/js/jquery.textarea_autosize.min.js
new file mode 100644
index 00000000..650dacd8
--- /dev/null
+++ b/SD-image/www/js/jquery.textarea_autosize.min.js
@@ -0,0 +1 @@
+!function(t,e,i,n){function h(e,i){this.element=e,this.$element=t(e),this.init()}var s="textareaAutoSize",o="plugin_"+s,l=function(t){return t.replace(/\s/g,"").length>0};h.prototype={init:function(){var i=(this.$element.outerHeight(),parseInt(this.$element.css("paddingBottom"))+parseInt(this.$element.css("paddingTop"))||0);l(this.element.value)&&this.$element.height(this.element.scrollHeight-i),this.$element.on("input keyup",function(n){var h=t(e),s=h.scrollTop();t(this).height(0).height(this.scrollHeight-i),h.scrollTop(s)})}},t.fn[s]=function(e){return this.each(function(){t.data(this,o)||t.data(this,o,new h(this,e))}),this}}(jQuery,window,document);
diff --git a/SD-image/www/js/model.js b/SD-image/www/js/model.js
index 33816737..d260a54e 100644
--- a/SD-image/www/js/model.js
+++ b/SD-image/www/js/model.js
@@ -1,4 +1,4 @@
-/* Interface logic for the Duet Web Control v1.08
+/* Interface logic for the Duet Web Control v1.10
*
* written by Christian Hammacher
*
@@ -6,7 +6,7 @@
* see http://www.gnu.org/licenses/gpl-2.0.html
*/
-var jsVersion = 1.08;
+var jsVersion = "1.10";
var sessionPassword = "reprap";
var translationWarning = false; // Set this to "true" if you want to look for missing translation entries
@@ -17,6 +17,7 @@ var settings = {
updateInterval: 250, // in ms
haltedReconnectDelay: 5000, // in ms
+ updateReconnectDelay: 15000, // in ms
extendedStatusInterval: 10, // nth status request will include extended values
maxRequestTime: 8000, // time to wait for a status response in ms
@@ -25,6 +26,7 @@ var settings = {
uppercaseGCode: true, // convert G-Codes to upper-case before sending them
useKiB: true, // display file sizes in KiB instead of KB
showFanControl: false, // show fan controls
+ showFanRPM: false, // show fan RPM in sensors
showATXControl: false, // show ATX control
confirmStop: false, // ask for confirmation when pressing Emergency STOP
useDarkTheme: false, // load dark theme by Fotomas
@@ -317,6 +319,10 @@ function updateStatus() {
// Status
var printing = false, paused = false;
switch (status.status) {
+ case 'F': // Flashing new firmware
+ setStatusLabel("Updating", "success");
+ break;
+
case 'H': // Halted
setStatusLabel("Halted", "danger");
break;
@@ -619,11 +625,7 @@ function updateStatus() {
$("#td_warmup_time").html(convertSeconds(status.warmUpDuration));
}
} else if (!printHasFinished) {
- if (status.printDuration > 0) {
- $("#td_warmup_time").html(convertSeconds(status.printDuration));
- } else {
- $("#td_warmup_time").html("n/a");
- }
+ $("#td_warmup_time").html("n/a");
}
// Current Layer Time
@@ -684,14 +686,20 @@ function updateStatus() {
drawTemperatureChart();
// Set timer for next status update
- if (status.status != 'H') {
- setTimeout(updateStatus, settings.updateInterval);
- } else {
- updateTaskLive = false;
+ if (status.status == 'F') {
+ isConnected = updateTaskLive = false;
+ log("info", "<strong>" + T("Updating Firmware...") + "</strong>");
+ setTimeout(function() {
+ connect(sessionPassword, false);
+ }, settings.updateReconnectDelay);
+ } else if (status.status == 'H') {
+ isConnected = updateTaskLive = false;
log("danger", "<strong>" + T("Emergency Stop!") + "</strong>");
setTimeout(function() {
connect(sessionPassword, false);
}, settings.haltedReconnectDelay);
+ } else {
+ setTimeout(updateStatus, settings.updateInterval);
}
// Save the last status response
@@ -711,7 +719,8 @@ function getConfigFile() {
success: function(response) {
if (response != "") {
configFile = response;
- $("#text_config").val(response).prop("readonly", false);
+ $("#div_config > h1").addClass("hidden");
+ $("#text_config").val(response).prop("readonly", false).trigger("input");
$("#row_save_settings").removeClass("hidden");
}
}
@@ -729,9 +738,10 @@ function getConfigResponse() {
if (configFile == undefined) {
if (response.hasOwnProperty("configFile")) {
$("#div_config > h1").addClass("hidden");
- $("#text_config").removeClass("hidden").prop("readonly", true).val(response.configFile);
+ $("#text_config").removeClass("hidden").prop("readonly", true).val(response.configFile).trigger("input");
} else {
- $("#div_config > h1").text(T("loading"));
+ $("#div_config > h1").removeClass("hidden").text(T("loading"));
+ $("#text_config").addClass("hidden");
}
}
@@ -1027,6 +1037,9 @@ function loadSettings(usingCookie) {
if (loadedSettings.hasOwnProperty("haltedReconnectDelay")) {
settings.haltedReconnectDelay = loadedSettings.haltedReconnectDelay;
}
+ if (loadedSettings.hasOwnProperty("updatedReconnectDelay")) {
+ settings.updateReconnectDelay = loadedSettings.updateReconnectDelay;
+ }
if (loadedSettings.hasOwnProperty("extendedStatusInterval")) {
settings.extendedStatusInterval = loadedSettings.extendedStatusInterval;
}
@@ -1056,6 +1069,9 @@ function loadSettings(usingCookie) {
if (loadedSettings.hasOwnProperty("showFanControl")) {
settings.showFanControl = loadedSettings.showFanControl;
}
+ if (loadedSettings.hasOwnProperty("showFanRPM")) {
+ settings.showFanRPM = loadedSettings.showFanRPM;
+ }
if (loadedSettings.hasOwnProperty("showATXControl")) {
settings.showATXControl = loadedSettings.showATXControl;
}
@@ -1107,6 +1123,7 @@ function saveSettings() {
settings.uppercaseGCode = $("#uppercase_gcode").is(":checked");
settings.useKiB = $("#use_kib").is(":checked");
settings.showFanControl = $("#fan_sliders").is(":checked");
+ settings.showFanRPM = $("#fan_rpm_display").is(":checked");
settings.showATXControl = $("#show_atx").is(":checked");
settings.confirmStop = $("#confirm_stop").is(":checked");
settings.useDarkTheme = $("#dark_theme").is(":checked");
@@ -1119,7 +1136,8 @@ function saveSettings() {
// UI Timing
settings.updateInterval = checkBoundaries($("#update_interval").val(), defaultSettings.updateInterval, 50);
settings.extendedStatusInterval = checkBoundaries($("#extended_status_interval").val(), defaultSettings.extendedStatusInterval, 1, 99999);
- settings.haltedReconnectDelay = checkBoundaries($("#reconnect_delay").val(), defaultSettings.haltedReconnectDelay, 1000);
+ settings.haltedReconnectDelay = checkBoundaries($("#reconnect_halt_delay").val(), defaultSettings.haltedReconnectDelay, 1000);
+ settings.updateReconnectDelay = checkBoundaries($("#reconnect_update_delay").val(), defaultSettings.updateReconnectDelay, 1000);
settings.maxRequestTime = checkBoundaries($("#ajax_timeout").val(), defaultSettings.maxRequestTime, 100);
settings.webcamInterval = checkBoundaries($("#webcam_interval").val(), defaultSettings.maxRequestTime, 100);
@@ -1165,7 +1183,7 @@ function saveSettings() {
configFile = $("#text_config").val();
var uploadFile = new File([configFile], "config.g", { type: "application/octet-stream" });
- startUpload("generic", [uploadFile]);
+ startUpload("generic", [uploadFile], false);
}
// Save Settings
@@ -1204,6 +1222,13 @@ function applySettings() {
$(".fan-control").addClass("hidden");
}
+ // Show/Hide Fan RPM
+ if (settings.showFanRPM) {
+ $(".fan-rpm").removeClass("hidden");
+ } else {
+ $(".fan-rpm").addClass("hidden");
+ }
+
// Show/Hide ATX Power
if (settings.showATXControl) {
$(".atx-control").removeClass("hidden");
@@ -1246,6 +1271,7 @@ function applySettings() {
$("#uppercase_gcode").prop("checked", settings.uppercaseGCode);
$("#use_kib").prop("checked", settings.useKiB);
$("#fan_sliders").prop("checked", settings.showFanControl);
+ $("#fan_rpm_display").prop("checked", settings.showFanRPM);
$("#show_atx").prop("checked", settings.showATXControl);
$("#confirm_stop").prop("checked", settings.confirmStop);
$("#dark_theme").prop("checked", settings.useDarkTheme);
@@ -1254,7 +1280,8 @@ function applySettings() {
// UI Timing
$("#update_interval").val(settings.updateInterval);
$("#extended_status_interval").val(settings.extendedStatusInterval);
- $("#reconnect_delay").val(settings.haltedReconnectDelay);
+ $("#reconnect_halt_delay").val(settings.haltedReconnectDelay);
+ $("#reconnect_update_delay").val(settings.updateReconnectDelay);
$("#ajax_timeout").val(settings.maxRequestTime);
$("#webcam_interval").val(settings.webcamInterval);
@@ -1287,9 +1314,9 @@ function applySettings() {
var uploadType, uploadFiles, uploadRows, uploadedFileCount;
var uploadTotalBytes, uploadedTotalBytes;
var uploadStartTime, uploadRequest, uploadFileSize, uploadFileName, uploadPosition;
-var uploadIncludedConfig;
+var uploadedDWC, uploadIncludedConfig, uploadFirmwareFile;
-function startUpload(type, files) {
+function startUpload(type, files, fromCallback) {
// Initialize some values
stopUpdates();
isUploading = true;
@@ -1300,7 +1327,11 @@ function startUpload(type, files) {
uploadTotalBytes += this.size;
});
uploadRows = [];
+ if (!fromCallback) {
+ uploadedDWC = false;
+ }
uploadIncludedConfig = false;
+ uploadFirmwareFile = undefined;
// Safety check for Upload and Print
if (type == "print" && files.length > 1) {
@@ -1309,45 +1340,49 @@ function startUpload(type, files) {
}
// Unzip files if necessary
- var containsZip = false;
- $.each(files, function() {
- if (this.name.match("\\.zip$")) {
- var fileReader = new FileReader();
- fileReader.onload = (function(theFile) {
- return function(e) {
- try {
- var zip = new JSZip(e.target.result);
-
- var zipFiles = [];
- $.each(zip.files, function(index, zipEntry) {
- if (!zipEntry.dir && zipEntry.name.match("\/\\.git") == null) {
- var zipName = zipEntry.name.split("/");
- zipName = zipName[zipName.length - 1];
-
- var unpackedFile = new File([zipEntry.asArrayBuffer()], zipName, { type: "application/octet-stream", lastModified: zipEntry.date });
- zipFiles.push(unpackedFile);
- }
- });
+ if (type == "macro" || type == "generic") {
+ var containsZip = false;
+ $.each(files, function() {
+ if (this.name.toLowerCase().match("\\.zip$") != null) {
+ uploadedDWC |= this.name.toLowerCase().match("^duetwebcontrol.*\\.zip") != null;
+
+ var fileReader = new FileReader();
+ fileReader.onload = (function(theFile) {
+ return function(e) {
+ try {
+ var zip = new JSZip(e.target.result);
+
+ var zipFiles = [];
+ $.each(zip.files, function(index, zipEntry) {
+ if (!zipEntry.dir && zipEntry.name.match("\/\\.git") == null && zipEntry.name.match("README") == null) {
+ var zipName = zipEntry.name.split("/");
+ zipName = zipName[zipName.length - 1];
+
+ var unpackedFile = new File([zipEntry.asArrayBuffer()], zipName, { type: "application/octet-stream", lastModified: zipEntry.date });
+ zipFiles.push(unpackedFile);
+ }
+ });
- if (zipFiles.length == 0) {
- showMessage("exclamation-sign", T("Error"), T("The archive {0} does not contain any files!", theFile.name), "md");
- } else {
- startUpload(type, zipFiles);
+ if (zipFiles.length == 0) {
+ showMessage("exclamation-sign", T("Error"), T("The archive {0} does not contain any files!", theFile.name), "md");
+ } else {
+ startUpload(type, zipFiles, true);
+ }
+ } catch(e) {
+ showMessage("exclamation-sign", T("Error"), T("Could not read contents of file {0}!", theFile.name), "md");
}
- } catch(e) {
- showMessage("exclamation-sign", T("Error"), T("Could not read contents of file {0}!", theFile.name), "md");
}
- }
- })(this);
- fileReader.readAsArrayBuffer(this);
+ })(this);
+ fileReader.readAsArrayBuffer(this);
- containsZip = true;
- return false;
+ containsZip = true;
+ return false;
+ }
+ });
+ if (containsZip) {
+ // We're relying on an async task which will trigger this method again when required
+ return;
}
- });
- if (containsZip) {
- // We're relying on an async task which will trigger this method again when required
- return;
}
// Reset modal dialog
@@ -1359,7 +1394,13 @@ function startUpload(type, files) {
// Add files to the table
$("#table_upload_files > tbody").remove();
$.each(files, function() {
- uploadIncludedConfig |= (this.name == "config.g") && (type == "generic");
+ if (type == "generic") {
+ uploadIncludedConfig |= (this.name == "config.g");
+ if (this.name.toUpperCase().match("^REPRAPFIRMWARE.*\.BIN") != null) {
+ uploadFirmwareFile = this.name;
+ }
+ }
+
var row = '<tr><td><span class="glyphicon glyphicon-asterisk"></span> ' + this.name + '</td>';
row += '<td>' + formatSize(this.size) + '</td>';
row += '<td><div class="progress"><div class="progress-bar progress-bar-info progress-bar-striped" role="progressbar"><span></span></div></div></td></tr>';
@@ -1553,13 +1594,53 @@ function uploadHasFinished() {
}
}
- // Ask for software reset if it's safe to do
- if (uploadIncludedConfig && lastStatusResponse != undefined && lastStatusResponse.status == 'I') {
- showConfirmationDialog(T("Reboot Duet?"), T("You have just uploaded a config file. Would you like to perform a software reset now?"), function() {
- sendGCode("M999");
+ // Ask for page reload if DWC has been updated
+ if (uploadedDWC) {
+ $("#modal_upload").modal("hide");
+ showConfirmationDialog(T("Reload Page?"), T("You have just updated Duet Web Control. Would you like to reload the page now?"), function() {
+ location.reload();
});
}
+ // Ask for software reset if it's safe to do
+ else if (lastStatusResponse != undefined && lastStatusResponse.status == 'I') {
+ if (uploadIncludedConfig) {
+ $("#modal_upload").modal("hide");
+ showConfirmationDialog(T("Reboot Duet?"), T("You have just uploaded a config file. Would you like to perform a software reset now?"), function() {
+ sendGCode("M999");
+ });
+ }
+
+ if (uploadFirmwareFile != undefined)
+ {
+ $("#modal_upload").modal("hide");
+ showConfirmationDialog(T("Perform Firmware Update?"), T("You have just uploaded a firmware file. Would you like to update your Duet now?"), function() {
+ if (uploadFirmwareFile.toUpperCase() != "REPRAPFIRMWARE.BIN")
+ {
+ // Firmware update filename is hardcoded in the IAP binary, so try to rename this one first
+ $.ajax("rr_move?old=" + encodeURIComponent("/sys/" + uploadFirmwareFile) + "&new=" + encodeURIComponent("/sys/RepRapFirmware.bin"), {
+ dataType: "json",
+ row: draggingObject,
+ success: function(response) {
+ if (response.err == 0) {
+ // Flashing can be performed now
+ sendGCode("M997");
+ } else {
+ // Something went wrong
+ showMessage("exclamation-sign", T("Error"), T("Could not rename firmware file!"), "md");
+ }
+ }
+ });
+ }
+ else
+ {
+ // Filename is okay, start flashing immediately
+ sendGCode("M997");
+ }
+ });
+ }
+ }
+
// Start polling again
startUpdates();
}
diff --git a/SD-image/www/js/viewmodel.js b/SD-image/www/js/viewmodel.js
index a4dc076a..4b328107 100644
--- a/SD-image/www/js/viewmodel.js
+++ b/SD-image/www/js/viewmodel.js
@@ -1,4 +1,4 @@
-/* Interface logic for the Duet Web Control v1.08
+/* Interface logic for the Duet Web Control v1.10
*
* written by Christian Hammacher
*
@@ -69,8 +69,8 @@ $(document).ready(function() {
resetGuiData();
updateGui();
- $("#web_version").append(", JS: " + jsVersion.toFixed(2));
- $("#text_config").autosize();
+ $("#web_version").append(", JS: " + jsVersion);
+ $("#text_config").textareaAutoSize();
loadSettings(false);
});
@@ -341,10 +341,10 @@ function updateWebcam(externalTrigger) {
webcamUpdating = false;
} else {
var newURL = settings.webcamURL;
- if (newURL.indexOf("?") != -1) {
- newURL = newURL + "?dummy=" + Math.random();
+ if (newURL.indexOf("?") == -1) {
+ newURL += "?dummy=" + Math.random();
} else {
- newURL = newURL + "&dummy=" + Math.random();
+ newURL += "&dummy=" + Math.random();
}
$("#img_webcam").attr("src", newURL);
@@ -990,7 +990,7 @@ $("#btn_reset_settings").click(function(e) {
var files = e.originalEvent.dataTransfer.files;
if (files != null && files.length > 0) {
// Start new file upload
- startUpload($(this).data("type"), files);
+ startUpload($(this).data("type"), files, false);
}
});
});
@@ -1071,7 +1071,7 @@ $("input[name='temp_selection']:radio").change(function() {
$("#input_file_upload").change(function(e) {
if (this.files.length > 0) {
// For POST uploads, we need file blobs
- startUpload($(this).data("type"), this.files);
+ startUpload($(this).data("type"), this.files, false);
}
});
@@ -1217,6 +1217,7 @@ $('a[href="#page_general"], a[href="#page_listitems"]').on('shown.bs.tab', funct
$('a[href="#page_config"]').on('shown.bs.tab', function() {
$("#row_save_settings, #btn_reset_settings").addClass("hidden");
+ $("#text_config").trigger("input");
getConfigFile();
});
diff --git a/SD-image/www/language.xml b/SD-image/www/language.xml
index 8597a805..1b3db060 100644
--- a/SD-image/www/language.xml
+++ b/SD-image/www/language.xml
@@ -111,7 +111,7 @@
<string t="Status Update Interval:">Aktualisierungsrate für Statusupdates:</string>
<string t="Extended Status Update after every:">Erweiterte Statusupdates nach jeder:</string>
<string t="th response">ten Antwort</string>
- <string t="Reconnect Delay on Halt:">Wiederverbindungsverzögerung nach NOT-AUS</string>
+ <string t="Reconnect Delay on Halt:">Wiederverbindungsverzögerung nach NOT-AUS:</string>
<string t="AJAX Timeout:">AJAX-Timeout:</string>
<string t="Send G-Code...">G-Code senden...</string>
<string t="Web Interface by Christian Hammacher">Web Interface von Christian Hammacher</string>
@@ -364,6 +364,19 @@
<string t="URL must point to an image file"></string>
<string t="Webcam Update Interval:">Webcam Updateintervall:</string>
<string t="Interval at which the webcam image on the Print Status page will be updated">Intervall nach dem das Webcam-Bild auf der Druckstatus-Seite aktualisiert wird</string>
+
+ <!-- New in v1.10 -->
+ <string t="Updating">Aktualisiere</string>
+ <string t="Updating Firmware...">Aktualisiere Firmware...</string>
+ <string t="Perform Firmware Update?">Firmwareaktualisierung durchführen?</string>
+ <string t="You have just uploaded a firmware file. Would you like to update your Duet now?">Sie haben gerade eine Firmwaredatei hochgeladen. Möchten Sie jetzt ihr Duet aktualisieren?</string>
+ <string t="Could not rename firmware file!">Konnte Firmwaredatei nicht umbenennen!</string>
+ <string t="Display fan RPM of a 4-pin PWM cooling fan in the sensors area">Umdrehungsanzahl pro Minute eines 4-pin PWM-Lüfters im Sensorenbereich anzeigen</string>
+ <string t="Show Fan RPM in Sensors">Lüfterumdrehungen pro Minute anzeigen</string>
+ <string t="Reconnect Delay on Firmware Update:">Wiederverbindungsverzögerung bei Firmwareaktualisierung:</string>
+ <string t="Time to wait for a reconnect when a new firmware is about to be flashed">Abzuwartende Zeit wenn eine neue Firmware geflasht wird</string>
+ <string t="Reload Page?">Seite neu laden?</string>
+ <string t="You have just updated Duet Web Control. Would you like to reload the page now?">Sie haben gerade Duet Web Control aktualisiert. Möchten Sie die Seite jetzt neu laden?</string>
</de>
<es name="Espanol">
<!-- JavaScript Strings -->
@@ -473,7 +486,7 @@
<string t="Status Update Interval:">Intervalo de actualización del estado:</string>
<string t="Extended Status Update after every:">Actualización completa de estado cada:</string>
<string t="th response">ésima respuesta</string>
- <string t="Reconnect Delay on Halt:">Retraso de reconexión tras Parada</string>
+ <string t="Reconnect Delay on Halt:">Retraso de reconexión tras Parada:</string>
<string t="AJAX Timeout:">Error AJAX:</string>
<string t="Send G-Code...">Enviar G-Code...</string>
<string t="Web Interface by Christian Hammacher">Interfaz Web de Christian Hammacher</string>
@@ -798,7 +811,7 @@
<string t="Status Update Interval:">Intervalle de mise à jour d'état:</string>
<string t="Extended Status Update after every:">Mise à jour complète de l'état après chaque:</string>
<string t="th response">ème réponse</string>
- <string t="Reconnect Delay on Halt:">Délai de reconnection après interruption</string>
+ <string t="Reconnect Delay on Halt:">Délai de reconnection après interruption:</string>
<string t="AJAX Timeout:">AJAX-Timeout:</string>
<string t="Send G-Code...">Envoie G-Code...</string>
<string t="Web Interface by Christian Hammacher">Web Interface par Christian Hammacher</string>
@@ -1151,7 +1164,7 @@
<string t="Status Update Interval:">Uppdateringsintervall för status:</string>
<string t="Extended Status Update after every:">Utökad status efter:</string>
<string t="th response">svar</string>
- <string t="Reconnect Delay on Halt:">Anslutningsfördröjning efter stopp</string>
+ <string t="Reconnect Delay on Halt:">Anslutningsfördröjning efter stopp:</string>
<string t="AJAX Timeout:">AJAX-timeout:</string>
<string t="Send G-Code...">Skicka G-kod...</string>
<string t="Web Interface by Christian Hammacher">Webgränssnitt av Christian Hammacher</string>
@@ -1504,7 +1517,7 @@
<string t="Status Update Interval:">状æ€æ›´æ–°é—´éš”:</string>
<string t="Extended Status Update after every:">æ¯æ¬¡åŽå»¶ç»­æ›´æ–°çŠ¶æ€:</string>
<string t="th response">å“应 </string>
- <string t="Reconnect Delay on Halt:">中间åœæ­¢åŽå†è¿žæŽ¥</string>
+ <string t="Reconnect Delay on Halt:">中间åœæ­¢åŽå†è¿žæŽ¥:</string>
<string t="AJAX Timeout:">AJAX 超时:</string>
<string t="Send G-Code...">å‘é€G-Code ...</string>
<string t="Web Interface by Christian Hammacher">Christian Hammacher网络控制界é¢ç‰ˆæœ¬ï¼Œè¥¿å®‰é”æ™®3D打å°æœºå…¬å¸ï¼ˆRepRapPro China)翻译 </string>
diff --git a/SD-image/www/reprap.htm b/SD-image/www/reprap.htm
index 5ffee930..82221ac4 100644
--- a/SD-image/www/reprap.htm
+++ b/SD-image/www/reprap.htm
@@ -1,10 +1,10 @@
<!DOCTYPE html>
-<!-- Duet Web Control v1.08
-
- written by Christian Hammacher
-
- licensed under the terms of the GPL v2
- see http://www.gnu.org/licenses/gpl-2.0.html
+<!-- Duet Web Control v1.10
+
+ written by Christian Hammacher
+
+ licensed under the terms of the GPL v2
+ see http://www.gnu.org/licenses/gpl-2.0.html
-->
<html lang="en">
<head>
@@ -13,9 +13,9 @@
<meta name="viewport" content="width=device-width">
<meta name="description" content="Web Interface for Duet Electronics (+ Extension)">
<meta name="author" content="Christian Hammacher">
-
+
<title id="title">Duet Web Control</title>
-
+
<!-- Favicon -->
<link rel="shortcut icon" href="favicon.ico">
<!-- CSS -->
@@ -24,7 +24,7 @@
<link href="css/bootstrap-slider.min.css" rel="stylesheet">
<link href="css/defaults.css" rel="stylesheet">
</head>
-
+
<body>
<!-- Navigation Bar -->
<nav class="navbar navbar-default">
@@ -43,14 +43,13 @@
</button>
</div>
<span class="label label-default label-status pull-right navbar-label visible-xs">Disconnected</span>
-
+
<a class="navbar-brand visible-xs">
- <!-- <img class="brand-sm" src="img/rrp_small.png"> -->
<span class="machine-name">Duet Web Control</span>
<abbr title="Some axes are not homed" class="hidden home-warning"><span class="glyphicon glyphicon-alert"></span></abbr>
</a>
</div>
-
+
<!-- Regular navbar entries -->
<div class="collapse navbar-collapse">
<button type="button" class="btn btn-connect btn-info navbar-btn navbar-left"><span class="glyphicon glyphicon-log-in"></span> <span>Connect</span></button>
@@ -59,7 +58,7 @@
<span class="glyphicon glyphicon-dashboard"></span>
</button>
</div>
-
+
<form class="navbar-form padding-small navbar-left hidden-sm gcode-input">
<div class="input-group">
<input type="text" class="form-control" placeholder="Send G-Code..." disabled>
@@ -71,27 +70,22 @@
</div>
<button type="submit" class="btn btn-default disabled" title="Send G-Code from input"><span class="glyphicon glyphicon-send"></span> Send</button>
</form>
-
+
<button type="button" class="btn btn-default btn-upload navbar-btn padding-small navbar-left hidden-xs hidden-sm disabled" title="Upload &amp; Print one or more G-Code files (drag&amp;drop is supported as well)" data-type="print" data-style="btn-default"><span class="glyphicon glyphicon-cloud-upload"></span> Upload &amp; Print</button>
-
+
<button type="button" class="btn btn-danger btn-emergency-stop navbar-btn navbar-right disabled" title="Enforces an immediate software reset (M112+M999)">Emergency STOP</button>
<span class="label label-default label-status navbar-label navbar-right">Disconnected</span>
-
+
<ul class="nav nav-justified hidden-xs">
<li>
<a href="#" class="navbar-brand">
- <!--
- <img class="brand-sm" src="img/rrp_small.png">
- <img class="brand-lg" src="img/rrp_large.png">
- <span class="brand-lg">-</span>
- -->
<span class="machine-name">Duet Web Control</span>
<abbr title="Some axes are not homed" class="hidden home-warning small"><span class="glyphicon glyphicon-alert"></span></abbr>
</a>
</li>
</ul>
</div>
-
+
<!-- Collapsed navbar entries for mobile devices -->
<div class="nav collapse" id="mobile-navbar">
<form class="gcode-input">
@@ -137,7 +131,7 @@
</div>
</div>
</nav>
-
+
<!-- Main Content -->
<div class="container-fluid">
<!-- Info Panels -->
@@ -176,7 +170,7 @@
</ul>
</div>
</div>
-
+
<table class="table table-bordered table-condensed table-centered-cells" id="table_heaters">
<tr>
<th></th>
@@ -184,7 +178,7 @@
<th>Active</th>
<th>Standby</th>
</tr>
-
+
<tr id="tr_head_1">
<th>
<a href="#">Heater 1<span class="caret hidden"></span></a>
@@ -212,7 +206,7 @@
</div>
</td>
</tr>
-
+
<tr id="tr_head_2">
<th>
<a href="#">Heater 2<span class="caret hidden"></span></a>
@@ -240,7 +234,7 @@
</div>
</td>
</tr>
-
+
<tr id="tr_head_3" class="hidden">
<th>
<a href="#">Heater 3<span class="caret hidden"></span></a>
@@ -268,7 +262,7 @@
</div>
</td>
</tr>
-
+
<tr id="tr_head_4" class="hidden">
<th>
<a href="#">Heater 4<span class="caret hidden"></span></a>
@@ -296,7 +290,7 @@
</div>
</td>
</tr>
-
+
<tr id="tr_head_5" class="hidden">
<th>
<a href="#">Heater 5<span class="caret hidden"></span></a>
@@ -324,7 +318,7 @@
</div>
</td>
</tr>
-
+
<tr id="tr_head_6" class="hidden">
<th>
<a href="#">Heater 6<span class="caret hidden"></span></a>
@@ -352,7 +346,7 @@
</div>
</td>
</tr>
-
+
<tr id="tr_bed">
<th>
<a href="#">Bed</a>
@@ -371,7 +365,7 @@
</td>
<td></td>
</tr>
-
+
<tr id="tr_chamber" class="hidden">
<th>
Chamber
@@ -393,14 +387,14 @@
</table>
</div>
</div>
-
+
<!-- Temperature Chart -->
<div class="hidden-xs hidden-sm col-md-3 col-lg-3" id="div_temp_chart">
<div class="panel panel-info panel-status">
<div class="panel-heading">
<span>Temperature Chart</span>
</div>
-
+
<div class="panel-body panel-chart">
<div id="chart_temp" title="Time vs. Temperature">
<!-- See JS -->
@@ -408,14 +402,14 @@
</div>
</div>
</div>
-
+
<!-- Machine Status -->
<div class="col-xs-12 col-sm-7 col-md-4 col-lg-5" id="div_status">
<div class="panel panel-info panel-status">
<div class="panel-heading">
<span>Machine Status</span>
</div>
-
+
<table class="table table-bordered table-condensed table-centered-cells table-status">
<tr>
<th rowspan="2" class="th-status">Head Position</th>
@@ -429,7 +423,7 @@
<td id="td_z">n/a</td>
</tr>
</table>
-
+
<table class="table table-bordered table-condensed table-centered-cells table-status">
<tr>
<th rowspan="2" class="th-status">
@@ -456,22 +450,22 @@
<td id="td_extr_6" class="hidden-md hidden extr-6">n/a</td>
</tr>
</table>
-
+
<table class="table table-bordered table-condensed table-centered-cells table-status">
<tr>
<th rowspan="2" class="th-status">Sensors</th>
<th>Z-Probe</th>
- <th class="fan-control">Fan RPM</th>
+ <th class="fan-rpm hidden">Fan RPM</th>
</tr>
<tr>
<td id="td_probe" title="Current Z-Probe reading on a scale between 0 and 1024">n/a</td>
- <td id="td_fanrpm" class="fan-control" title="Current Fan RPM (needs a 4-pin PWM fan and proper wiring)">n/a</td>
+ <td id="td_fanrpm" class="fan-rpm hidden" title="Current Fan RPM (needs a 4-pin PWM fan and proper wiring)">n/a</td>
</tr>
</table>
</div>
</div>
</div>
-
+
<!-- Content -->
<div class="row">
<!-- Sidebar -->
@@ -490,7 +484,7 @@
<li class="navitem navitem-help hidden"><a class="navlink" href="#" data-target="help"><span class="glyphicon glyphicon-question-sign"></span> Help</a></li>
</ul>
</div>
-
+
<!-- Page Contents -->
<div class="col-xs-12 col-sm-9 col-md-10 col-lg-10" id="main_content">
<!-- Control Page -->
@@ -503,7 +497,7 @@
<span>Head Movement</span>
<button data-gcode="G32" class="pull-right btn btn-primary btn-xs gcode visible-lg disabled" id="btn_bed_compensation" title="Perform automatic bed compensation / calibration (G32)">Auto Bed Compensation</button>
</div>
-
+
<div class="panel-body">
<div class="hidden-lg btn-group btn-group-justified" id="mobile_home_buttons">
<div class="btn-group" role="group"><button data-home="all" class="btn btn-primary disabled">Home All</button></div>
@@ -584,17 +578,17 @@
</table>
</div>
</div>
-
+
<div class="alert alert-warning hidden home-warning">
<span class="glyphicon glyphicon-warning-sign"></span>
<span id="unhomed_warning">The following axes are not homed: <strong id="unhomed_axes">X, Y, Z</strong></span>
</div>
-
+
<div class="panel panel-default" id="panel_extrude">
<div class="panel-heading">
<span>Extruder Control</span>
</div>
-
+
<div class="panel-body">
<div class="col-xs-5 no-horizontal-padding">
<label>Feed amount in mm:</label>
@@ -619,7 +613,7 @@
</label>
</div>
</div>
-
+
<div class="col-xs-5">
<label>Feedrate in mm/sec:</label>
<div class="btn-group btn-group-justified" data-toggle="buttons">
@@ -640,7 +634,7 @@
</label>
</div>
</div>
-
+
<div class="col-xs-2 no-horizontal-padding">
<div class="btn-group-vertical btn-group-vertical-justified">
<div class="btn-group">
@@ -654,20 +648,20 @@
</div>
</div>
</div>
-
+
<div class="col-xs-6 col-sm-6 col-md-4 col-lg-3 col-right">
<div class="panel panel-default" id="panel_macro_buttons">
<div class="panel-heading">
<span>User-Defined Macros</span>
</div>
-
+
<div class="panel-body text-center">
<h4 class="text-muted">Go to the Macros page to define your own actions</h4>
<div class="btn-group-vertical btn-group-vertical-justified"></div>
</div>
</div>
</div>
-
+
<div class="col-xs-6 col-sm-6 col-md-4 col-lg-3 col-right">
<div class="panel panel-default" id="panel_control_misc">
<div class="panel-heading">
@@ -704,7 +698,7 @@
</div>
</div>
</div>
-
+
<!-- Print Status -->
<div id="page_print" class="page">
<div class="row row-progress hidden">
@@ -722,7 +716,7 @@
</div>
</div>
</div>
-
+
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-6 col-md-push-3 col-lg-7 col-lg-push-2">
<div id="panel_webcam" class="panel panel-default hidden">
@@ -745,12 +739,12 @@
</div>
</div>
</div>
-
+
<div id="panel_print_info" class="panel panel-default">
<div class="panel-heading">
<span>Collected Data</span>
</div>
-
+
<table class="table table-bordered table-condensed">
<tr>
<th>Warm-Up Time</th>
@@ -766,12 +760,12 @@
</tr>
</table>
</div>
-
+
<div class="panel panel-default">
<div class="panel-heading">
<span>Estimations</span>
</div>
-
+
<table class="table table-bordered table-condensed" id="table_estimations">
<tr>
<th>Based on</th>
@@ -794,7 +788,7 @@
</table>
</div>
</div>
-
+
<div class="col-xs-6 col-sm-6 col-md-3 col-md-pull-6 col-lg-2 col-lg-pull-7 col-left">
<div class="panel panel-default" id="panel_print_control">
<div class="panel-heading">
@@ -809,17 +803,17 @@
<button class="btn btn-danger" id="btn_cancel" title="Cancel paused print (M0)">Cancel</button>
</div>
</div>
-
+
<div class="checkbox fan-control disabled">
<label title="Enforce a predefined fan value">
<input type="checkbox" id="override_fan" disabled/> Override Fan Value
</label>
</div>
-
+
<div class="fan-control">
<input id="slider_fan_print" type="text" title="Set current fan value (via M106)" disabled/>
</div>
-
+
<div class="checkbox disabled">
<label title="Run M1 when the print has finished">
<input type="checkbox" id="auto_sleep" disabled/> Enable Auto-Sleep
@@ -827,7 +821,7 @@
</div>
</div>
</div>
-
+
<div class="panel panel-default">
<div class="panel-heading">
<span>File Information</span>
@@ -848,7 +842,7 @@
</div>
</div>
</div>
-
+
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3 col-right">
<div class="panel panel-default">
<div class="panel-heading">
@@ -859,7 +853,7 @@
</div>
</div>
</div>
-
+
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3 col-right">
<div class="panel panel-default">
<div class="panel-heading">
@@ -909,7 +903,7 @@
</div>
</div>
</div>
-
+
<!-- G-Code Console -->
<div id="page_console" class="page">
<div class="row">
@@ -940,7 +934,7 @@
</div>
</form>
</div>
-
+
<div class="row">
<div class="col-md-12">
<div class="container-fluid" id="console_log">
@@ -948,7 +942,7 @@
</div>
</div>
</div>
-
+
<!-- G-Code Files -->
<div id="page_files" class="page">
<div class="row">
@@ -971,7 +965,7 @@
</table>
<h1 class="text-center text-muted">Connect to your Duet to display G-Code files</h1>
-
+
<table class="table table-bordered table-files table-hover table-striped hidden" id="table_gcode_files">
<thead>
<tr>
@@ -990,7 +984,7 @@
</div>
</div>
</div>
-
+
<!-- Macro Files -->
<div id="page_macros" class="page">
<div class="row">
@@ -1011,10 +1005,10 @@
</td>
</tr>
</table>
-
+
<h1 class="text-center text-muted">Connect to your Duet to display Macro files</h1>
</div>
-
+
<div class="col-xs-12 col-sm-10 col-sm-push-1 col-md-8 col-md-push-2 col-lg-6 col-lg-push-3">
<table class="table table-bordered table-files table-hover table-striped hidden" id="table_macro_files">
<thead>
@@ -1030,7 +1024,7 @@
</div>
</div>
</div>
-
+
<!-- Settings -->
<div id="page_settings" class="page">
<form id="frm_settings">
@@ -1042,7 +1036,7 @@
<li><a href="#page_machine" data-toggle="tab">Machine Properties</a></li>
<li><a href="#page_tools" data-toggle="tab">Tools</a></li>
</ul>
-
+
<!-- Page Selector (Mobile) -->
<ul class="nav nav-tabs visible-xs visible-sm">
<li class="active"><a href="#page_general" data-toggle="tab">General</a></li>
@@ -1056,7 +1050,7 @@
</ul>
</li>
</ul>
-
+
<div class="tab-content">
<!-- General Settings -->
<div class="tab-pane row active" id="page_general">
@@ -1077,7 +1071,7 @@
</tr>
<tr>
<th>Web Interface Version:</th>
- <td id="web_version">HTML: 1.08</td>
+ <td id="web_version">HTML: 1.10</td>
</tr>
</table>
<span>Web Interface by Christian Hammacher<br/>Licensed under the terms of the <a href="http://www.gnu.org/licenses/gpl-2.0.html" target="blank">GPL v2</a></span>
@@ -1086,6 +1080,16 @@
</div>
</div>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <span>Webcam Integration</span>
+ </div>
+ <div class="panel-body">
+ <label for="webcam_url">Optional URL to an external Webcam:</label>
+ <input id="webcam_url" class="form-control" type="text" title="URL must point to an image file">
+ </div>
+ </div>
+
<div class="panel panel-default hidden-xs hidden-sm">
<div class="panel-heading">
<span>File Upload for /sys and /www Directories</span>
@@ -1098,24 +1102,14 @@
</div>
</div>
</div>
-
- <div class="panel panel-default">
- <div class="panel-heading">
- <span>Webcam Integration</span>
- </div>
- <div class="panel-body">
- <label for="webcam_url">Optional URL to an external Webcam:</label>
- <input id="webcam_url" class="form-control" type="text" title="URL must point to an image file">
- </div>
- </div>
</div>
-
+
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
<div class="panel panel-default">
<div class="panel-heading">
<span>Appearance and Behavior</span>
</div>
-
+
<div class="panel-body">
<div class="checkbox">
<label title="Connect automatically to the Duet once the web interface is ready">
@@ -1148,6 +1142,11 @@
</label>
</div>
<div class="checkbox">
+ <label title="Display fan RPM of a 4-pin PWM cooling fan in the sensors area">
+ <input type="checkbox" id="fan_rpm_display" checked /> Show Fan RPM in Sensors
+ </label>
+ </div>
+ <div class="checkbox">
<label title="Show a small panel on the Control page to toggle ATX power">
<input type="checkbox" id="show_atx" checked /> Show ATX Power control
</label>
@@ -1162,13 +1161,13 @@
<input type="checkbox" id="dark_theme" /> Use dark theme
</label>
</div>
-
+
<label id="label_feedrate" for="move_feedrate">Default Move Button Feedrate:</label>
<div class="input-group">
<input type="number" id="move_feedrate" class="form-control" title="Default Feedrate for Head Movement controls" value="6000">
<span class="input-group-addon">mm/min</span>
</div>
-
+
<label id="label_language">Language:</label>
<div class="dropdown" id="dropdown_language">
<button class="btn btn-default dropdown-toggle" type="button" id="btn_language" data-language="en" data-toggle="dropdown">
@@ -1182,32 +1181,38 @@
</div>
</div>
</div>
-
+
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4 col-right">
<div class="panel panel-default" id="panel_ui_timing">
<div class="panel-heading">
<span>UI Timing</span>
</div>
-
+
<div class="panel-body">
<label for="update_interval">Status Update Interval:</label>
<div class="input-group">
<input type="number" id="update_interval" class="form-control" title="Interval at which status updates are requested">
<span class="input-group-addon">ms</span>
</div>
-
+
<label for="extended_status_interval">Extended Status Update after every:</label>
<div class="input-group">
<input type="number" id="extended_status_interval" class="form-control" title="Extended status updates are requested after each n-th status response">
<span class="input-group-addon">th response</span>
</div>
-
- <label for="reconnect_delay">Reconnect Delay on Halt:</label>
+
+ <label for="reconnect_halt_delay">Reconnect Delay on Halt:</label>
+ <div class="input-group">
+ <input type="number" id="reconnect_halt_delay" class="form-control" title="Time to wait for a reconnect after an emergency stop was reported">
+ <span class="input-group-addon">ms</span>
+ </div>
+
+ <label for="reconnect_update_delay">Reconnect Delay on Firmware Update:</label>
<div class="input-group">
- <input type="number" id="reconnect_delay" class="form-control" title="Time to wait for a reconnect after an emergency stop was reported">
+ <input type="number" id="reconnect_update_delay" class="form-control" title="Time to wait for a reconnect when a new firmware is about to be flashed">
<span class="input-group-addon">ms</span>
</div>
-
+
<label for="ajax_timeout">AJAX Timeout:</label>
<div class="input-group">
<input type="number" id="ajax_timeout" class="form-control" title="Maximum time to wait for an AJAX request">
@@ -1223,7 +1228,7 @@
</div>
</div>
</div>
-
+
<!-- List Items -->
<div class="tab-pane row" id="page_listitems">
<div class="col-md-4 col-lg-4 col-left">
@@ -1241,7 +1246,7 @@
</thead>
</table>
</div>
-
+
<div class="panel panel-default">
<div class="panel-heading">
<span>Add new G-Code</span>
@@ -1284,13 +1289,13 @@
<input type="radio" name="temp_selection" value="standby">Standby Temperatures
</label>
</div>
-
+
<ul class="list-group temp-list" id="ul_active_temps"></ul>
-
+
<ul class="list-group temp-list hidden" id="ul_standby_temps"></ul>
</div>
</div>
-
+
<div class="panel panel-default">
<div class="panel-heading">
<span>Add new Temperature</span>
@@ -1322,7 +1327,7 @@
<ul class="list-group temp-list" id="ul_bed_temps"></ul>
</div>
</div>
-
+
<div class="panel panel-default">
<div class="panel-heading">
<span>Add new Temperature</span>
@@ -1346,7 +1351,7 @@
</div>
</div>
</div>
-
+
<!-- Configuration File -->
<div class="tab-pane row" id="page_config">
<div class="col-md-12" id="div_config">
@@ -1354,7 +1359,7 @@
<textarea readonly id="text_config" class="hidden"></textarea>
</div>
</div>
-
+
<!-- Machine Properties -->
<div class="tab-pane row" id="page_machine">
<div class="col-xs-12 col-sm-12 col-md-9 col-lg-10">
@@ -1497,13 +1502,13 @@
<dt>Motor Idle Timeout:</dt>
<dd id="dd_idle_timeout">n/a</dd>
</dl>
-
+
<button id="btn_fw_diagnostics" type="button" class="btn btn-info"><span class="glyphicon glyphicon-dashboard"></span> Firmware Diagnostics</button>
</div>
</div>
</div>
</div>
-
+
<!-- Tools -->
<div class="tab-pane row" id="page_tools">
<div class="col-sm-12 col-md-7 col-lg-6">
@@ -1578,7 +1583,7 @@
</div>
</div>
</div>
-
+
<div class="row" id="row_save_settings">
<div class="col-md-12 text-center">
<button type="submit" class="btn btn-lg btn-primary" title="Apply new settings from this page"><span class="glyphicon glyphicon-ok"></span> Apply Settings</button>
@@ -1589,13 +1594,13 @@
</div>
</div>
</div>
-
+
<!-- Sidebar Continuation -->
<div class="row">
<div class="col-sm-3 col-md-2 col-lg-2 sidebar sidebar-continuation"></div>
</div>
</div>
-
+
<!-- Modal dialogs -->
<div class="modal fade" id="modal_pass_input">
<div class="modal-dialog modal-md">
@@ -1619,7 +1624,7 @@
</div>
</div>
</div>
-
+
<div class="modal fade" id="modal_confirmation">
<div class="modal-dialog modal-md">
<div class="modal-content">
@@ -1637,7 +1642,7 @@
</div>
</div>
</div>
-
+
<div class="modal fade" id="modal_textinput">
<div class="modal-dialog modal-sm">
<div class="modal-content">
@@ -1660,7 +1665,7 @@
</div>
</div>
</div>
-
+
<div class="modal fade" id="modal_message">
<div class="modal-dialog modal-md">
<div class="modal-content">
@@ -1677,7 +1682,7 @@
</div>
</div>
</div>
-
+
<input type="file" id="input_file_upload" class="hidden" multiple>
<div class="modal fade" id="modal_upload" data-backdrop="static">
<div class="modal-dialog modal-md">
@@ -1708,18 +1713,18 @@
<div id="layer_tooltip"></div>
<!-- Bootstrap core JavaScript
- ================================================== -->
- <!-- Placed at the end of the document so the pages load faster -->
- <script src="js/jquery-2.1.3.min.js"></script>
- <script src="js/jquery.autosize.min.js"></script>
- <script src="js/jquery.flot.min.js"></script>
- <script src="js/jquery.flot.resize.min.js"></script>
- <script src="js/jquery.flot.navigate.min.js"></script>
- <script src="js/bootstrap.min.js"></script>
- <script src="js/bootstrap-slider.min.js"></script>
- <script src="js/jszip.min.js"></script>
- <script src="js/model.js"></script>
- <script src="js/viewmodel.js"></script>
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="js/jquery-2.1.3.min.js"></script>
+ <script src="js/jquery.textarea_autosize.min.js"></script>
+ <script src="js/jquery.flot.min.js"></script>
+ <script src="js/jquery.flot.resize.min.js"></script>
+ <script src="js/jquery.flot.navigate.min.js"></script>
+ <script src="js/bootstrap.min.js"></script>
+ <script src="js/bootstrap-slider.min.js"></script>
+ <script src="js/jszip.min.js"></script>
+ <script src="js/model.js"></script>
+ <script src="js/viewmodel.js"></script>
</body>
</html>
<!-- vim: ts=4 sw=4
diff --git a/src/Configuration.h b/src/Configuration.h
index 596bc348..5e70943a 100644
--- a/src/Configuration.h
+++ b/src/Configuration.h
@@ -26,17 +26,18 @@ Licence: GPL
#define NAME "RepRapFirmware"
#ifndef VERSION
-#define VERSION "1.09t-dc42-beta2"
+#define VERSION "1.09x-dc42-beta3"
#endif
#ifndef DATE
-#define DATE "2016-03-10"
+#define DATE "2016-03-11"
#endif
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"
#define FLASH_SAVE_ENABLED (1)
+// If enabled, the following control the use of the optional ExternalDrivers module
//#define EXTERNAL_DRIVERS (1)
//#define FIRST_EXTERNAL_DRIVE (4)
@@ -169,6 +170,9 @@ const float FILAMENT_WIDTH = 1.75; // Millimetres
#define EOF_STRING "<!-- **EoF** -->"
+#define IAP_UPDATE_FILE "iap.bin"
+#define IAP_FIRMWARE_FILE "RepRapFirmware.bin"
+
// List defaults
const char LIST_SEPARATOR = ':';
diff --git a/src/DDA.cpp b/src/DDA.cpp
index 50008729..29490685 100644
--- a/src/DDA.cpp
+++ b/src/DDA.cpp
@@ -96,6 +96,7 @@ bool DDA::Init(const float nextMove[], EndstopChecks ce, bool doMotorMapping, Fi
isDeltaMovement = false;
}
+ isPrintingMove = false;
bool realMove = false, xyMoving = false;
const bool isSpecialDeltaMove = (move->IsDeltaMode() && !doMotorMapping);
float accelerations[DRIVES];
@@ -146,6 +147,7 @@ bool DDA::Init(const float nextMove[], EndstopChecks ce, bool doMotorMapping, Fi
if (drive >= AXES && xyMoving)
{
+ isPrintingMove = true; // we have both movement and extrusion
float compensationTime = reprap.GetPlatform()->GetElasticComp(drive);
if (compensationTime > 0.0)
{
@@ -724,20 +726,17 @@ bool DDA::Start(uint32_t tim)
if (extrusions != 0 || retractions != 0)
{
const unsigned int prohibitedMovements = reprap.GetProhibitedExtruderMovements(extrusions, retractions);
- if (prohibitedMovements != 0)
+ for (DriveMovement **dmpp = &firstDM; *dmpp != nullptr; )
{
- for (DriveMovement **dmpp = &firstDM; *dmpp != nullptr; )
+ bool thisDriveExtruding = (*dmpp)->drive >= AXES;
+ if (thisDriveExtruding && (prohibitedMovements & (1 << ((*dmpp)->drive - AXES))) != 0)
{
- bool thisDriveExtruding = (*dmpp)->drive >= AXES;
- if (thisDriveExtruding && (prohibitedMovements & (1 << ((*dmpp)->drive - AXES))) != 0)
- {
- *dmpp = (*dmpp)->nextDM;
- }
- else
- {
- extruding = extruding || thisDriveExtruding;
- dmpp = &((*dmpp)->nextDM);
- }
+ *dmpp = (*dmpp)->nextDM;
+ }
+ else
+ {
+ extruding = extruding || thisDriveExtruding;
+ dmpp = &((*dmpp)->nextDM);
}
}
}
diff --git a/src/DDA.h b/src/DDA.h
index 22ba97b3..14054650 100644
--- a/src/DDA.h
+++ b/src/DDA.h
@@ -43,6 +43,7 @@ public:
float CalcTime() const; // Calculate the time needed for this move (used for simulation)
bool HasStepError() const;
bool CanPause() const { return canPause; }
+ bool IsPrintingMove() const { return isPrintingMove; } // Return true if this involves both XY movement and extrusion
DDAState GetState() const { return state; }
DDA* GetNext() const { return next; }
@@ -93,10 +94,11 @@ private:
DDA *prev; // The previous one in the ring
volatile DDAState state; // what state this DDA is in
- bool endCoordinatesValid; // True if endCoordinates can be relied on
- bool isDeltaMovement; // True if this is a delta printer movement
- bool canPause; // True if we can pause at the end of this move
- bool goingSlow; // True if we have reduced speed during homing
+ uint8_t endCoordinatesValid : 1; // True if endCoordinates can be relied on
+ uint8_t isDeltaMovement : 1; // True if this is a delta printer movement
+ uint8_t canPause : 1; // True if we can pause at the end of this move
+ uint8_t goingSlow : 1; // True if we have reduced speed during homing
+ uint8_t isPrintingMove : 1; // True if this move includes XY movement and extrusion
EndstopChecks endStopsToCheck; // Which endstops we are checking on this move
// We are on a half-word boundary here, so expect 2 bytes of padding to be inserted at this point
diff --git a/src/GCodeBuffer.cpp b/src/GCodeBuffer.cpp
index a4240d5f..be8538b3 100644
--- a/src/GCodeBuffer.cpp
+++ b/src/GCodeBuffer.cpp
@@ -12,12 +12,9 @@
// This class stores a single G Code and provides functions to allow it to be parsed
GCodeBuffer::GCodeBuffer(Platform* p, const char* id)
+ : platform(p), identity(id), checksumRequired(false), writingFileDirectory(nullptr), toolNumberAdjust(0)
{
- platform = p;
- identity = id;
- writingFileDirectory = NULL; // Has to be done here as Init() is called every line.
- toolNumberAdjust = 0;
- checksumRequired = false;
+ Init();
}
void GCodeBuffer::Init()
@@ -25,18 +22,17 @@ void GCodeBuffer::Init()
gcodePointer = 0;
readPointer = -1;
inComment = false;
- state = idle;
+ state = GCodeState::idle;
}
int GCodeBuffer::CheckSum() const
{
- int cs = 0;
- for (int i = 0; gcodeBuffer[i] != '*' && gcodeBuffer[i] != 0; i++)
+ uint8_t cs = 0;
+ for (size_t i = 0; gcodeBuffer[i] != '*' && gcodeBuffer[i] != 0; i++)
{
- cs = cs ^ gcodeBuffer[i];
+ cs = cs ^ (uint8_t)gcodeBuffer[i];
}
- cs &= 0xff; // Defensive programming...
- return cs;
+ return (int)cs;
}
// Add a byte to the code being assembled. If false is returned, the code is
@@ -54,7 +50,7 @@ bool GCodeBuffer::Put(char c)
{
inComment = true;
}
- else if (c == '\n' || !c)
+ else if (c == '\n' || c == 0)
{
gcodeBuffer[gcodePointer] = 0;
if (reprap.Debug(moduleGcodes) && gcodeBuffer[0] && !writingFileDirectory) // Don't bother with blank/comment lines
@@ -79,13 +75,13 @@ bool GCodeBuffer::Put(char c)
// Strip out the line number and checksum
gcodePointer = 0;
- while (gcodeBuffer[gcodePointer] != ' ' && gcodeBuffer[gcodePointer])
+ while (gcodeBuffer[gcodePointer] != ' ' && gcodeBuffer[gcodePointer] != 0)
{
gcodePointer++;
}
// Anything there?
- if (!gcodeBuffer[gcodePointer])
+ if (gcodeBuffer[gcodePointer] == 0)
{
// No...
gcodeBuffer[0] = 0;
@@ -96,21 +92,22 @@ bool GCodeBuffer::Put(char c)
// Yes...
gcodePointer++;
int gp2 = 0;
- while (gcodeBuffer[gcodePointer] != '*' && gcodeBuffer[gcodePointer])
+ while (gcodeBuffer[gcodePointer] != '*' && gcodeBuffer[gcodePointer] != 0)
{
gcodeBuffer[gp2] = gcodeBuffer[gcodePointer++];
gp2++;
}
gcodeBuffer[gp2] = 0;
}
- else if (checksumRequired)
+ else if (checksumRequired || IsEmpty())
{
+ // Checksum not found or buffer empty - cannot do anything
gcodeBuffer[0] = 0;
Init();
return false;
}
Init();
- state = executing;
+ state = GCodeState::executing;
return true;
}
else if (!inComment || writingFileDirectory)
@@ -118,7 +115,7 @@ bool GCodeBuffer::Put(char c)
gcodeBuffer[gcodePointer++] = c;
if (gcodePointer >= (int)GCODE_LENGTH)
{
- platform->Message(GENERIC_MESSAGE, "G-Code buffer length overflow.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: G-Code buffer length overflow.\n");
gcodePointer = 0;
gcodeBuffer[0] = 0;
}
@@ -175,7 +172,7 @@ float GCodeBuffer::GetFValue()
{
if (readPointer < 0)
{
- platform->Message(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode float before a search.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode float before a search.\n");
readPointer = -1;
return 0.0;
}
@@ -190,7 +187,7 @@ const void GCodeBuffer::GetFloatArray(float a[], size_t& returnedLength)
{
if(readPointer < 0)
{
- platform->Message(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode float array before a search.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode float array before a search.\n");
readPointer = -1;
returnedLength = 0;
return;
@@ -202,7 +199,7 @@ const void GCodeBuffer::GetFloatArray(float a[], size_t& returnedLength)
{
if(length >= returnedLength) // Array limit has been set in here
{
- platform->MessageF(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode float array that is too long: %s\n", gcodeBuffer);
+ platform->MessageF(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode float array that is too long: %s\n", gcodeBuffer);
readPointer = -1;
returnedLength = 0;
return;
@@ -244,7 +241,7 @@ const void GCodeBuffer::GetLongArray(long l[], size_t& returnedLength)
{
if(readPointer < 0)
{
- platform->Message(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode long array before a search.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode long array before a search.\n");
readPointer = -1;
return;
}
@@ -255,7 +252,7 @@ const void GCodeBuffer::GetLongArray(long l[], size_t& returnedLength)
{
if(length >= returnedLength) // Array limit has been set in here
{
- platform->MessageF(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode long array that is too long: %s\n", gcodeBuffer);
+ platform->MessageF(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode long array that is too long: %s\n", gcodeBuffer);
readPointer = -1;
returnedLength = 0;
return;
@@ -284,7 +281,7 @@ const char* GCodeBuffer::GetString()
{
if (readPointer < 0)
{
- platform->Message(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode string before a search.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode string before a search.\n");
readPointer = -1;
return "";
}
@@ -317,9 +314,9 @@ const char* GCodeBuffer::GetUnprecedentedString(bool optional)
readPointer = -1;
if (optional)
{
- return NULL;
+ return nullptr;
}
- platform->Message(GENERIC_MESSAGE, "GCodes: String expected but not seen.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: String expected but not seen.\n");
return gcodeBuffer; // Good idea?
}
@@ -334,7 +331,7 @@ long GCodeBuffer::GetLValue()
{
if (readPointer < 0)
{
- platform->Message(GENERIC_MESSAGE, "GCodes: Attempt to read a GCode int before a search.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: Attempt to read a GCode int before a search.\n");
readPointer = -1;
return 0;
}
@@ -346,7 +343,7 @@ long GCodeBuffer::GetLValue()
// Return true if this buffer contains a poll request or empty request that can be executed while macros etc. from another source are being completed
bool GCodeBuffer::IsPollRequest()
{
- if (state == executing)
+ if (state == GCodeState::executing)
{ if (IsEmpty())
{
return true;
diff --git a/src/GCodeBuffer.h b/src/GCodeBuffer.h
index 885912f3..0bcd25fc 100644
--- a/src/GCodeBuffer.h
+++ b/src/GCodeBuffer.h
@@ -42,7 +42,7 @@ class GCodeBuffer
private:
- enum State { idle, executing, paused };
+ enum class GCodeState { idle, executing, paused };
int CheckSum() const; // Compute the checksum (if any) at the end of the G Code
Platform* platform; // Pointer to the RepRap's controlling class
char gcodeBuffer[GCODE_LENGTH]; // The G Code
@@ -51,16 +51,15 @@ class GCodeBuffer
int readPointer; // Where in the buffer to read next
bool inComment; // Are we after a ';' character?
bool checksumRequired; // True if we only accept commands with a valid checksum
- State state; // Idle, executing or paused
+ GCodeState state; // Idle, executing or paused
const char* writingFileDirectory; // If the G Code is going into a file, where that is
int toolNumberAdjust; // The adjustment to tool numbers in commands we receive
};
// Get an Int after a G Code letter
-
inline int GCodeBuffer::GetIValue()
{
- return (int)GetLValue();
+ return static_cast<int>(GetLValue());
}
inline const char* GCodeBuffer::Buffer() const
@@ -70,27 +69,27 @@ inline const char* GCodeBuffer::Buffer() const
inline bool GCodeBuffer::Active() const
{
- return state == executing;
+ return state == GCodeState::executing;
}
inline void GCodeBuffer::SetFinished(bool f)
{
- state = (f) ? idle : executing;
+ state = (f) ? GCodeState::idle : GCodeState::executing;
}
inline void GCodeBuffer::Pause()
{
- if (state == executing)
+ if (state == GCodeState::executing)
{
- state = paused;
+ state = GCodeState::paused;
}
}
inline void GCodeBuffer::Resume()
{
- if (state == paused)
+ if (state == GCodeState::paused)
{
- state = executing;
+ state = GCodeState::executing;
}
}
diff --git a/src/GCodes.cpp b/src/GCodes.cpp
index 14454f80..887a3cf8 100644
--- a/src/GCodes.cpp
+++ b/src/GCodes.cpp
@@ -33,7 +33,7 @@ const char GCodes::axisLetters[AXES] =
const size_t gcodeReplyLength = 2048; // long enough to pass back a reasonable number of files in response to M20
GCodes::GCodes(Platform* p, Webserver* w) :
- platform(p), active(false), webserver(w), stackPointer(0), auxGCodeReply(nullptr)
+ platform(p), active(false), webserver(w), stackPointer(0), auxGCodeReply(nullptr), isFlashing(false)
{
httpGCode = new GCodeBuffer(platform, "web: ");
telnetGCode = new GCodeBuffer(platform, "telnet: ");
@@ -3176,11 +3176,11 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
{
seen = true;
long heaters[HEATERS];
- unsigned int numH = HEATERS;
+ size_t numH = HEATERS;
gb->GetLongArray(heaters, numH);
// Note that M106 H-1 disables thermostatic mode. The following code implements that automatically.
uint16_t hh = 0;
- for (unsigned int h = 0; h < numH; ++h)
+ for (size_t h = 0; h < numH; ++h)
{
const int hnum = heaters[h];
if (hnum >= 0 && hnum < HEATERS)
@@ -4945,6 +4945,28 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
}
break;
+ case 997: // Perform firmware update
+ if (!platform->GetMassStorage()->FileExists(platform->GetSysDir(), IAP_FIRMWARE_FILE))
+ {
+ platform->MessageF(GENERIC_MESSAGE, "Error: Firmware file \"%s\" not found in sys directory\n", IAP_FIRMWARE_FILE);
+ break;
+ }
+ if (!platform->GetMassStorage()->FileExists(platform->GetSysDir(), IAP_UPDATE_FILE))
+ {
+ platform->MessageF(GENERIC_MESSAGE, "Error: IAP file \"%s\" not found in sys directory\n", IAP_UPDATE_FILE);
+ break;
+ }
+
+ isFlashing = true;
+ if (!DoDwellTime(2.0))
+ {
+ // wait a second so all HTTP clients are notified
+ return false;
+ }
+ platform->UpdateFirmware();
+ isFlashing = false; // should never get here, but leave this here in case an error has occurred
+ break;
+
case 998:
// The input handling code replaces the gcode by this when it detects a checksum error.
// Since we have no way of asking for the line to be re-sent, just report an error.
@@ -5091,6 +5113,11 @@ bool GCodes::IsResuming() const
return topState == GCodeState::resuming1 || topState == GCodeState::resuming2 || topState == GCodeState::resuming3;
}
+bool GCodes::IsRunning() const
+{
+ return !IsPaused() && !IsPausing() && !IsResuming();
+}
+
const char *GCodes::TranslateEndStopResult(EndStopHit es)
{
switch (es)
diff --git a/src/GCodes.h b/src/GCodes.h
index d6ab97a6..1abe8a3c 100644
--- a/src/GCodes.h
+++ b/src/GCodes.h
@@ -103,12 +103,14 @@ public:
float GetTotalRawExtrusion() const { return rawExtruderTotal; } // Get the total extrusion since start of print, all drives
bool HaveAux() const { return auxDetected; } // Any device on the AUX line?
+ bool IsFlashing() const { return isFlashing; } // Is a new firmware binary going to be flashed?
OutputBuffer *GetAuxGCodeReply(); // Returns cached G-Code reply for AUX devices and clears its reference
uint32_t GetAuxSeq() { return auxSeq; }
bool IsPaused() const;
bool IsPausing() const;
bool IsResuming() const;
+ bool IsRunning() const;
bool AllAxesAreHomed() const; // Return true if all axes are homed
bool DoFileMacro(const char* fileName, bool reportMissing = true); // Run a GCode macro in a file, optionally report error if not found
@@ -223,6 +225,7 @@ private:
uint32_t auxSeq; // Sequence number for AUX devices
bool simulating;
float simulationTime;
+ bool isFlashing; // Is a new firmware binary going to be flashed?
FilePosition filePos; // The position we got up to in the file being printed
FilePosition moveFilePos; // Saved version of filePos for the next real move to be processed
};
diff --git a/src/MassStorage.cpp b/src/MassStorage.cpp
index 103119ef..4fba5b26 100644
--- a/src/MassStorage.cpp
+++ b/src/MassStorage.cpp
@@ -297,6 +297,14 @@ bool MassStorage::FileExists(const char *file) const
return (f_stat(file, &fil) == FR_OK);
}
+bool MassStorage::FileExists(const char *directory, const char *fileName) const
+{
+ const char *location = (directory != nullptr)
+ ? platform->GetMassStorage()->CombineName(directory, fileName)
+ : fileName;
+ return FileExists(location);
+}
+
// Check if the specified directory exists
bool MassStorage::DirectoryExists(const char *path) const
{
diff --git a/src/MassStorage.h b/src/MassStorage.h
index 10cf4a9d..06972a47 100644
--- a/src/MassStorage.h
+++ b/src/MassStorage.h
@@ -9,31 +9,32 @@ class MassStorage
{
public:
- bool FindFirst(const char *directory, FileInfo &file_info);
- bool FindNext(FileInfo &file_info);
- const char* GetMonthName(const uint8_t month);
- const char* CombineName(const char* directory, const char* fileName);
- bool Delete(const char* directory, const char* fileName);
- bool MakeDirectory(const char *parentDir, const char *dirName);
- bool MakeDirectory(const char *directory);
- bool Rename(const char *oldFilename, const char *newFilename);
- bool FileExists(const char *file) const;
- bool DirectoryExists(const char *path) const;
- bool DirectoryExists(const char* directory, const char* subDirectory);
+ bool FindFirst(const char *directory, FileInfo &file_info);
+ bool FindNext(FileInfo &file_info);
+ const char* GetMonthName(const uint8_t month);
+ const char* CombineName(const char* directory, const char* fileName);
+ bool Delete(const char* directory, const char* fileName);
+ bool MakeDirectory(const char *parentDir, const char *dirName);
+ bool MakeDirectory(const char *directory);
+ bool Rename(const char *oldFilename, const char *newFilename);
+ bool FileExists(const char *file) const;
+ bool FileExists(const char* directory, const char *fileName) const;
+ bool DirectoryExists(const char *path) const;
+ bool DirectoryExists(const char* directory, const char* subDirectory);
friend class Platform;
protected:
- MassStorage(Platform* p);
- void Init();
+ MassStorage(Platform* p);
+ void Init();
private:
- Platform* platform;
- FATFS fileSystem;
- DIR findDir;
- char combinedName[FILENAME_LENGTH + 1];
+ Platform* platform;
+ FATFS fileSystem;
+ DIR findDir;
+ char combinedName[FILENAME_LENGTH + 1];
};
#endif
diff --git a/src/Move.cpp b/src/Move.cpp
index 4cf650b8..a9321da5 100644
--- a/src/Move.cpp
+++ b/src/Move.cpp
@@ -1253,6 +1253,15 @@ void Move::GetCurrentMachinePosition(float m[DRIVES + 1], bool disableMotorMappi
return ((float)(endpoint))/reprap.GetPlatform()->DriveStepsPerUnit(drive);
}
+// Is filament being extruded?
+bool Move::IsExtruding() const
+{
+ cpu_irq_disable();
+ bool rslt = currentDda != nullptr && currentDda->IsPrintingMove();
+ cpu_irq_enable();
+ return rslt;
+}
+
// Return the transformed machine coordinates
void Move::GetCurrentUserPosition(float m[DRIVES + 1], uint8_t moveType) const
{
diff --git a/src/Move.h b/src/Move.h
index 1ffea7dc..2f24b514 100644
--- a/src/Move.h
+++ b/src/Move.h
@@ -102,6 +102,8 @@ public:
static int32_t MotorEndPointToMachine(size_t drive, float coord); // Convert a single motor position to number of steps
static float MotorEndpointToPosition(int32_t endpoint, size_t drive); // Convert number of motor steps to motor position
+ bool IsExtruding() const; // Is filament being extruded?
+
private:
enum class IdleState : uint8_t { idle, busy, timing };
diff --git a/src/Network.cpp b/src/Network.cpp
index 7ae53f86..bde1f6a9 100644
--- a/src/Network.cpp
+++ b/src/Network.cpp
@@ -40,27 +40,62 @@
#include "RepRapFirmware.h"
+extern "C"
+{
+#include "ethernet_sam.h"
+
+#include "lwipopts.h"
+
#ifdef LWIP_STATS
#include "lwip/src/include/lwip/stats.h"
#endif
-extern "C"
-{
-#include "lwipopts.h"
#include "lwip/src/include/lwip/tcp.h"
+#include "lwip/src/include/lwip/tcp_impl.h"
+
#include "contrib/apps/netbios/netbios.h"
+#include "contrib/apps/mdns/mdns_responder.h"
}
+static volatile bool lwipLocked = false;
+static volatile bool hasLink = false;
+
static tcp_pcb *http_pcb = nullptr;
static tcp_pcb *ftp_main_pcb = nullptr;
static tcp_pcb *ftp_pasv_pcb = nullptr;
static tcp_pcb *telnet_pcb = nullptr;
+static struct mdns_service mdns_services[] = {
+ {
+ .name = "\x05_echo\x04_tcp\x05local",
+ .port = 7,
+ },
+ {
+ .name = "\x05_http\x04_tcp\x05local",
+ .port = DEFAULT_HTTP_PORT,
+ },
+ {
+ .name = "\x04_ftp\x04_tcp\x05local",
+ .port = FTP_PORT
+ },
+ {
+ .name = "\x07_telnet\x04_tcp\x05local",
+ .port = TELNET_PORT
+ }
+};
+
+const size_t MDNS_HTTP_SERVICE_INDEX = 1; // Index of the mDNS HTTP service above
+
+static const char *mdns_txt_records[] = {
+ "product=" NAME,
+ "version=" VERSION,
+ NULL
+};
+
static bool closingDataPort = false;
-static volatile bool lwipLocked = false;
+static ConnectionState *sendingConnection = nullptr;
-static NetworkTransaction *sendingTransaction = nullptr;
static uint32_t sendingWindow32[(TCP_WND + 3)/4]; // should be 32-bit aligned for efficiency
static inline char* sendingWindow() { return reinterpret_cast<char*>(sendingWindow32); }
static uint16_t sendingWindowSize, sentDataOutstanding;
@@ -68,18 +103,6 @@ static uint8_t sendingRetries;
static uint16_t httpPort = DEFAULT_HTTP_PORT;
-// Called only by LWIP to put out a message.
-// May be called from C as well as C++
-
-extern "C" void RepRapNetworkMessage(const char* s)
-{
-#ifdef LWIP_DEBUG
- reprap.GetPlatform()->Message(DEBUG_MESSAGE, s);
-#else
- reprap.GetPlatform()->Message(HOST_MESSAGE, s);
-#endif
-}
-
/*-----------------------------------------------------------------------------------*/
extern "C"
@@ -100,17 +123,32 @@ void UnlockLWIP()
lwipLocked = false;
}
-// Callback functions for the EMAC driver (called from ISR)
+// Callback functions for the EMAC driver
+
+// Callback to report when the link has gone up or down
+static void ethernet_status_callback(struct netif *netif)
+{
+ hasLink = netif_is_up(netif);
+ if (hasLink)
+ {
+ char ip[16];
+ ipaddr_ntoa_r(&(netif->ip_addr), ip, sizeof(ip));
+ reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network up, IP=%s\n", ip);
+ }
+ else
+ {
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network down\n");
+ }
+}
-static void emac_read_packet(uint32_t ul_status)
+// Called from ISR
+static void ethernet_rx_callback(uint32_t ul_status)
{
// Because the LWIP stack can become corrupted if we work with it in parallel,
// we may have to wait for the next Spin() call to read the next packet.
- if (ethernet_is_ready() && LockLWIP())
+ if (LockLWIP())
{
- do {
- // read all queued packets from the RX buffer
- } while (ethernet_read());
+ ethernet_task();
UnlockLWIP();
}
else
@@ -120,7 +158,7 @@ static void emac_read_packet(uint32_t ul_status)
}
}
-// Callback functions called by LWIP (may be called from ISR)
+// Callback functions for LWIP (may be called from ISR)
static void conn_err(void *arg, err_t err)
{
@@ -130,12 +168,13 @@ static void conn_err(void *arg, err_t err)
ConnectionState *cs = (ConnectionState*)arg;
if (cs != nullptr)
{
- reprap.GetNetwork()->ConnectionClosed(cs, false); // tell the higher levels about the error
- if (sendingTransaction == cs->sendingTransaction)
+ if (reprap.Debug(moduleNetwork))
{
- sendingTransaction = nullptr;
- sentDataOutstanding = 0;
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: This connection has a valid CS\n");
}
+
+ // Tell the higher levels about the error
+ reprap.GetNetwork()->ConnectionClosed(cs, false);
}
}
@@ -144,33 +183,45 @@ static void conn_err(void *arg, err_t err)
static err_t conn_poll(void *arg, tcp_pcb *pcb)
{
ConnectionState *cs = (ConnectionState*)arg;
- if (cs != nullptr && sendingTransaction != nullptr && cs == sendingTransaction->GetConnection())
+ if (cs == sendingConnection)
{
- // We tried to send data, but didn't receive an ACK within reasonable time.
-
+ // Data could not be sent in time, check if the connection has to be timed out
sendingRetries++;
- if (sendingRetries == 4)
+ if (sendingRetries == TCP_MAX_SEND_RETRIES)
{
- reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Poll received error!\n");
+ reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Poll couldn't send data after %d retries!\n", TCP_MAX_SEND_RETRIES);
tcp_abort(pcb);
return ERR_ABRT;
}
- // Try to send the remaining data once again
+ // Do we have enough space left for sending? Third-party apps may be sending data as well, so check this first
+ if (tcp_sndbuf(pcb) < TCP_WND)
+ {
+ if (reprap.Debug(moduleNetwork))
+ {
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Could not send data because not enough sending space is available\n");
+ }
+ return ERR_MEM;
+ }
+ // Try to send the remaining data once again
err_t err = tcp_write(pcb, sendingWindow() + (sendingWindowSize - sentDataOutstanding), sentDataOutstanding, 0);
if (err == ERR_OK)
{
- tcp_output(pcb);
+ err = tcp_output(pcb);
}
- else
+
+ if (ERR_IS_FATAL(err))
{
- reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: tcp_write in conn_poll failed with code %d\n", err);
+ reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Failed to write data in conn_poll (code %d)\n", err);
tcp_abort(pcb);
return ERR_ABRT;
}
}
-
+ else
+ {
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Mismatched pcb in conn_poll!\n");
+ }
return ERR_OK;
}
@@ -178,12 +229,22 @@ static err_t conn_poll(void *arg, tcp_pcb *pcb)
static err_t conn_sent(void *arg, tcp_pcb *pcb, u16_t len)
{
- LWIP_UNUSED_ARG(pcb);
-
ConnectionState *cs = (ConnectionState*)arg;
- if (cs != nullptr)
+ if (cs == sendingConnection)
+ {
+ if (sentDataOutstanding > len)
+ {
+ sentDataOutstanding -= len;
+ }
+ else
+ {
+ tcp_poll(pcb, nullptr, TCP_WRITE_TIMEOUT / TCP_SLOW_INTERVAL / TCP_MAX_SEND_RETRIES);
+ sendingConnection = nullptr;
+ }
+ }
+ else
{
- reprap.GetNetwork()->SentPacketAcknowledged(cs, len);
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Mismatched pcb in conn_sent!\n");
}
return ERR_OK;
}
@@ -197,21 +258,31 @@ static err_t conn_recv(void *arg, tcp_pcb *pcb, pbuf *p, err_t err)
{
if (cs->pcb != pcb)
{
- reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Mismatched pcb!\n");
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Mismatched pcb in conn_recv!\n");
tcp_abort(pcb);
return ERR_ABRT;
}
+ bool processingOk = true;
if (p != nullptr)
{
// Tell higher levels that we are receiving data
- reprap.GetNetwork()->ReceiveInput(p, cs);
+ processingOk = reprap.GetNetwork()->ReceiveInput(p, cs);
}
else if (cs->persistConnection)
{
- // This is called when the connection has been gracefully closed, but LWIP doesn't close these
- // connections automatically. That's why we must do it once all packets have been read.
- reprap.GetNetwork()->ConnectionClosedGracefully(cs);
+ // Tell higher levels that a connection has been closed
+ processingOk = reprap.GetNetwork()->ConnectionClosedGracefully(cs);
+ }
+
+ if (!processingOk)
+ {
+ if (p != nullptr)
+ {
+ pbuf_free(p);
+ }
+ tcp_abort(pcb);
+ return ERR_ABRT;
}
}
@@ -225,10 +296,6 @@ static err_t conn_accept(void *arg, tcp_pcb *pcb, err_t err)
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
- tcp_setprio(pcb, TCP_PRIO_MIN);
-
- //RepRapNetworkMessage("conn_accept\n");
-
/* Allocate a new ConnectionState for this connection */
ConnectionState *cs = reprap.GetNetwork()->ConnectionAccepted(pcb);
if (cs == nullptr)
@@ -255,7 +322,6 @@ static err_t conn_accept(void *arg, tcp_pcb *pcb, err_t err)
tcp_arg(pcb, cs); // tell LWIP that this is the structure we wish to be passed for our callbacks
tcp_recv(pcb, conn_recv); // tell LWIP that we wish to be informed of incoming data by a call to the conn_recv() function
tcp_err(pcb, conn_err);
- tcp_poll(pcb, conn_poll, 4);
return ERR_OK;
}
@@ -345,58 +411,68 @@ void Network::Spin()
return;
}
- if (state == NetworkActive)
+ if (state == NetworkActive && hasLink)
{
// See if we can read any packets
if (readingData)
{
- do {
- // read all queued packets from the RX buffer
- } while (ethernet_read());
-
- if (ethernet_is_ready())
- {
- readingData = false;
- ethernet_set_rx_callback(&emac_read_packet);
- }
+ ethernet_task();
+ readingData = false;
+ ethernet_set_rx_callback(&ethernet_rx_callback);
}
// See if we can send anything
NetworkTransaction *r = writingTransactions;
- if (r != nullptr && r->Send())
+ if (r != nullptr && sendingConnection == nullptr)
{
- // We're done, free up this transaction
-
- ConnectionState *cs = r->cs;
- NetworkTransaction *rn = r->nextWrite;
- writingTransactions = r->next;
- AppendTransaction(&freeTransactions, r);
-
- // If there is more data to write on this connection, do it next time
-
- if (cs != nullptr)
+ if (r->next != nullptr)
{
- cs->sendingTransaction = rn;
+ // Data is supposed to be sent to another connection and the last packet has
+ // been acknowledged. Rotate the sending transactions so every client is
+ // served even while big files are being sent.
+ NetworkTransaction *rn = r->next;
+ writingTransactions = rn;
+ AppendTransaction(&writingTransactions, r);
+ r = rn;
}
- if (rn != nullptr)
+
+ if (r->Send())
{
- PrependTransaction(&writingTransactions, rn);
+ // We're done, free up this transaction
+ ConnectionState *cs = r->cs;
+ NetworkTransaction *rn = r->nextWrite;
+ writingTransactions = r->next;
+ AppendTransaction(&freeTransactions, r);
+
+ // If there is more data to write on this connection, do it next time
+
+ if (cs != nullptr)
+ {
+ cs->sendingTransaction = rn;
+ }
+ if (rn != nullptr)
+ {
+ PrependTransaction(&writingTransactions, rn);
+ }
}
}
}
- else if (state == NetworkPostInitializing && establish_ethernet_link())
+ else if (state == NetworkPostInitializing && ethernet_establish_link())
{
- start_ethernet(platform->IPAddress(), platform->NetMask(), platform->GateWay());
- ethernet_set_rx_callback(&emac_read_packet);
+ start_ethernet(platform->IPAddress(), platform->NetMask(), platform->GateWay(), &ethernet_status_callback);
+ ethernet_set_rx_callback(&ethernet_rx_callback);
httpd_init();
ftpd_init();
telnetd_init();
+
netbios_init();
+ mdns_responder_init(mdns_services, ARRAY_SIZE(mdns_services), mdns_txt_records);
+ mdns_announce(); // NB: Wireless bridges like the TP-Link WR702N don't route incoming IGMP packets, so send an mDNS announcement as a fall-back option
- state = NetworkActive;
+ state = isEnabled ? NetworkActive : NetworkInactive;
}
UnlockLWIP();
@@ -405,7 +481,7 @@ void Network::Spin()
void Network::Interrupt()
{
- if (isEnabled && LockLWIP())
+ if (isEnabled && state == NetworkActive && LockLWIP())
{
ethernet_timers_update();
UnlockLWIP();
@@ -500,26 +576,12 @@ void Network::SetHttpPort(uint16_t port)
tcp_bind(pcb, IP_ADDR_ANY, port);
http_pcb = tcp_listen(pcb);
tcp_accept(http_pcb, conn_accept);
- }
- httpPort = port;
-}
-void Network::SentPacketAcknowledged(ConnectionState *cs, unsigned int len)
-{
- if (cs != nullptr && sendingTransaction != nullptr && cs == sendingTransaction->GetConnection())
- {
- if (sentDataOutstanding > len)
- {
- sentDataOutstanding -= len;
- }
- else
- {
- sendingTransaction = nullptr;
- sentDataOutstanding = 0;
- }
+ // Update mDNS services
+ mdns_services[MDNS_HTTP_SERVICE_INDEX].port = port;
+ mdns_announce();
}
-
-// debugPrintf("Network SentPacketAcknowledged: invalid cs=%08x\n", (unsigned int)cs);
+ httpPort = port;
}
// This is called when a connection is being established and returns an initialised ConnectionState instance.
@@ -549,11 +611,10 @@ ConnectionState *Network::ConnectionAccepted(tcp_pcb *pcb)
return cs;
}
-// This is called when a connection is being closed or has gone down.
-// It must set the state of any NetworkTransaction that refers to it to connection lost.
+// This is called when a connection is being closed or has gone down unexpectedly
void Network::ConnectionClosed(ConnectionState* cs, bool closeConnection)
{
- // make sure these connections are not reused
+ // Make sure these connections are not reused. Remove all references to it
if (cs == dataCs)
{
dataCs = nullptr;
@@ -566,22 +627,25 @@ void Network::ConnectionClosed(ConnectionState* cs, bool closeConnection)
{
telnetCs = nullptr;
}
+ if (cs == sendingConnection)
+ {
+ sendingConnection = nullptr;
+ }
- // inform the Webserver that we are about to remove an existing connection
+ // Close it if requested
tcp_pcb *pcb = cs->pcb;
- if (pcb != nullptr)
+ if (pcb != nullptr && closeConnection)
{
- reprap.GetWebserver()->ConnectionLost(cs);
- if (closeConnection)
- {
- tcp_arg(pcb, nullptr);
- tcp_sent(pcb, nullptr);
- tcp_recv(pcb, nullptr);
- tcp_poll(pcb, nullptr, 4);
- tcp_close(pcb);
- cs->pcb = nullptr;
- }
+ tcp_arg(pcb, nullptr);
+ tcp_sent(pcb, nullptr);
+ tcp_recv(pcb, nullptr);
+ tcp_poll(pcb, nullptr, TCP_WRITE_TIMEOUT / TCP_SLOW_INTERVAL / TCP_MAX_SEND_RETRIES);
+ tcp_close(pcb);
}
+ cs->pcb = nullptr;
+
+ // Inform the Webserver that we are about to remove an existing connection
+ reprap.GetWebserver()->ConnectionLost(cs);
// cs points to a connection state block that the caller is about to release, so we need to stop referring to it.
// There may be one NetworkTransaction in the writing or closing list referring to it, and possibly more than one in the ready list.
@@ -604,18 +668,20 @@ void Network::ConnectionClosed(ConnectionState* cs, bool closeConnection)
freeConnections = cs;
}
-void Network::ConnectionClosedGracefully(ConnectionState *cs)
+// This enqueues a new transaction to indicate a graceful reset. Do this to keep the time line of incoming transactions valid
+bool Network::ConnectionClosedGracefully(ConnectionState *cs)
{
NetworkTransaction* r = freeTransactions;
if (r == nullptr)
{
platform->Message(HOST_MESSAGE, "Network::ConnectionClosedGracefully() - no free transactions!\n");
- return;
+ return false;
}
+
freeTransactions = r->next;
r->Set(nullptr, cs, disconnected);
-
AppendTransaction(&readyTransactions, r);
+ return true;
}
bool Network::Lock()
@@ -638,13 +704,13 @@ void Network::ReadPacket()
readingData = true;
}
-void Network::ReceiveInput(pbuf *pb, ConnectionState* cs)
+bool Network::ReceiveInput(pbuf *pb, ConnectionState* cs)
{
NetworkTransaction* r = freeTransactions;
if (r == nullptr)
{
platform->Message(HOST_MESSAGE, "Network::ReceiveInput() - no free transactions!\n");
- return;
+ return false;
}
freeTransactions = r->next;
@@ -652,6 +718,7 @@ void Network::ReceiveInput(pbuf *pb, ConnectionState* cs)
AppendTransaction(&readyTransactions, r);
// debugPrintf("Network - input received\n");
+ return true;
}
// This is called by the web server to get a new received packet.
@@ -723,7 +790,7 @@ void Network::WaitForDataConection()
const uint8_t *Network::IPAddress() const
{
- return reinterpret_cast<const uint8_t*>(&ethernet_get_configuration()->ip_addr.addr);
+ return ethernet_get_ipaddress();
}
void Network::SetIPAddress(const uint8_t ipAddress[], const uint8_t netmask[], const uint8_t gateway[])
@@ -732,6 +799,9 @@ void Network::SetIPAddress(const uint8_t ipAddress[], const uint8_t netmask[], c
{
// This performs IP changes on-the-fly
ethernet_set_configuration(ipAddress, netmask, gateway);
+
+ // Announce mDNS services again
+ mdns_announce();
}
}
@@ -835,7 +905,8 @@ bool Network::AcquireTransaction(ConnectionState *cs)
}
// See if we're already writing on this connection
- NetworkTransaction *lastTransaction = cs->sendingTransaction;
+ NetworkTransaction *firstTransaction = cs->sendingTransaction;
+ NetworkTransaction *lastTransaction = firstTransaction;
if (lastTransaction != nullptr)
{
while (lastTransaction->nextWrite != nullptr)
@@ -846,7 +917,7 @@ bool Network::AcquireTransaction(ConnectionState *cs)
// Then check if this transaction is valid and safe to use
NetworkTransaction *transactionToUse;
- if (lastTransaction != nullptr && sendingTransaction != lastTransaction && lastTransaction->fileBeingSent == nullptr)
+ if (firstTransaction != nullptr && firstTransaction != lastTransaction && lastTransaction->fileBeingSent == nullptr)
{
transactionToUse = lastTransaction;
}
@@ -886,44 +957,39 @@ void Network::SetHostname(const char *name)
}
}
- if (i)
+ if (i != 0)
{
hostname[i] = 0;
}
else
{
+ // Don't allow empty hostnames
strcpy(hostname, HOSTNAME);
}
+
+ if (state == NetworkActive)
+ {
+ mdns_update_hostname();
+ }
}
-// Initialise a ConnectionState for a new connection
+//***************************************************************************************************
+
+// ConnectionState class
+
void ConnectionState::Init(tcp_pcb *p)
{
pcb = p;
+ localPort = p->local_port;
+ remoteIPAddress = p->remote_ip.addr;
+ remotePort = p->remote_port;
next = nullptr;
sendingTransaction = nullptr;
persistConnection = true;
}
-// Get local port from a ConnectionState
-uint16_t ConnectionState::GetLocalPort() const
-{
- return pcb->local_port;
-}
-
-// Get remote IP from a ConnectionState
-uint32_t ConnectionState::GetRemoteIP() const
-{
- return pcb->remote_ip.addr;
-}
-
-// Get remote port from a ConnectionState
-uint16_t ConnectionState::GetRemotePort() const
-{
- return pcb->remote_port;
-}
-
-// NetworkTransaction class members
+//***************************************************************************************************
+// NetworkTransaction class
NetworkTransaction::NetworkTransaction(NetworkTransaction *n) : next(n)
{
@@ -933,7 +999,7 @@ NetworkTransaction::NetworkTransaction(NetworkTransaction *n) : next(n)
void NetworkTransaction::Set(pbuf *p, ConnectionState *c, TransactionStatus s)
{
cs = c;
- pb = p;
+ pb = readingPb = p;
bufferLength = (p == nullptr) ? 0 : pb->tot_len;
status = s;
inputPointer = 0;
@@ -941,101 +1007,72 @@ void NetworkTransaction::Set(pbuf *p, ConnectionState *c, TransactionStatus s)
fileBeingSent = nullptr;
closeRequested = false;
nextWrite = nullptr;
- lastWriteTime = NAN;
waitingForDataConnection = false;
}
-// How many incoming bytes do we have to process?
-size_t NetworkTransaction::DataLength() const
-{
- return (pb == nullptr) ? 0 : pb->tot_len;
-}
-
-// Webserver calls this to read bytes that have come in from the network.
+// Read one char from the NetworkTransaction
bool NetworkTransaction::Read(char& b)
{
- if (LostConnection() || pb == nullptr)
+ if (LostConnection() || readingPb == nullptr)
{
+ b = 0;
return false;
}
- if (inputPointer == pb->len)
+ b = ((const char*)readingPb->payload)[inputPointer++];
+ if (inputPointer > readingPb->len)
{
- // See if there is another pbuf in the chain
- if (inputPointer < pb->tot_len)
- {
- pb = pbuf_dechain(pb);
- if (pb == nullptr)
- {
- return false;
- }
- inputPointer = 0;
- }
- else
- {
- return false;
- }
+ readingPb = readingPb->next;
+ inputPointer = 0;
}
-
- b = ((const char*)pb->payload)[inputPointer++];
return true;
}
-// Read an entire pbuf from the NetworkTransaction
-bool NetworkTransaction::ReadBuffer(char *&buffer, unsigned int &len)
+// Read data from the NetworkTransaction and return true on success
+bool NetworkTransaction::ReadBuffer(const char *&buffer, unsigned int &len)
{
- if (LostConnection() || pb == nullptr)
+ if (LostConnection() || readingPb == nullptr)
{
return false;
}
- if (inputPointer == pb->len)
+ if (inputPointer >= readingPb->len)
{
- // See if there is another pbuf in the chain
- if (inputPointer < pb->tot_len)
- {
- pb = pbuf_dechain(pb);
- if (pb == nullptr)
- {
- return false;
- }
- inputPointer = 0;
- }
- else
+ readingPb = readingPb->next;
+ inputPointer = 0;
+ if (readingPb == nullptr)
{
return false;
}
}
- len = pb->len;
- buffer = static_cast<char *>(pb->payload);
- inputPointer += len;
-
+ buffer = (const char*)readingPb->payload + inputPointer;
+ len = readingPb->len - inputPointer;
+ readingPb = readingPb->next;
+ inputPointer = 0;
return true;
}
-// Webserver calls this to write bytes that need to go out to the network
-
void NetworkTransaction::Write(char b)
{
if (CanWrite())
{
if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer))
{
+ // Should never get here
return;
}
sendBuffer->cat(b);
}
}
-// These functions attempt to store a whole string for sending.
-// It may be necessary to split it up into multiple SendBuffers.
void NetworkTransaction::Write(const char* s)
{
if (CanWrite())
{
if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer))
{
+ // Should never get here
return;
}
sendBuffer->cat(s);
@@ -1053,6 +1090,7 @@ void NetworkTransaction::Write(const char* s, size_t len)
{
if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer))
{
+ // Should never get here
return;
}
sendBuffer->cat(s, len);
@@ -1069,6 +1107,7 @@ void NetworkTransaction::Write(OutputBuffer *buffer)
}
else
{
+ // Don't keep buffers we can't send...
OutputBuffer::ReleaseAll(buffer);
}
}
@@ -1112,11 +1151,10 @@ void NetworkTransaction::SetFileToWrite(FileStore *file)
}
}
-// Send exactly one TCP window of data or return true if we can free up this object
+// Send exactly one TCP window of data and return true when done
bool NetworkTransaction::Send()
{
- // Free up this transaction if we either lost our connection or are supposed to close it now
-
+ // Free up this transaction if we either lost the connection or if it is supposed to be closed
if (LostConnection() || closeRequested)
{
if (fileBeingSent != nullptr)
@@ -1130,7 +1168,6 @@ bool NetworkTransaction::Send()
if (!LostConnection())
{
-// debugPrintf("NetworkTransaction is closing connection cs=%08x\n", (unsigned int)cs);
reprap.GetNetwork()->ConnectionClosed(cs, true);
}
@@ -1146,33 +1183,20 @@ bool NetworkTransaction::Send()
closingDataPort = false;
}
- sendingTransaction = nullptr;
- sentDataOutstanding = 0;
-
return true;
}
- // We're still waiting for data to be ACK'ed, so check timeouts here
- if (sentDataOutstanding != 0)
+ // Do we have enough space left for sending? Third-party apps may be sending data as well, so check this first
+ if (tcp_sndbuf(cs->pcb) < TCP_WND)
{
- if (!isnan(lastWriteTime))
+ if (reprap.Debug(moduleNetwork))
{
- float timeNow = reprap.GetPlatform()->Time();
- if (timeNow - lastWriteTime > TCP_WRITE_TIMEOUT)
- {
-// reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Timing out connection cs=%08x\n", (unsigned int)cs);
- tcp_abort(cs->pcb);
- cs->pcb = nullptr;
- }
- return false;
+ reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Could not send data because not enough sending space is available\n");
}
- }
- else
- {
- sendingTransaction = nullptr;
+ return false;
}
- // See if we can fill up the TCP window with some data chunks from our OutputBuffer instances
+ // Fill up the TCP window with some data chunks from our OutputBuffer instances
size_t bytesBeingSent = 0, bytesLeftToSend = TCP_WND;
while (sendBuffer != nullptr && bytesLeftToSend > 0)
{
@@ -1215,7 +1239,7 @@ bool NetworkTransaction::Send()
if (bytesBeingSent == 0)
{
- // If we have no data to send and fileBeingSent is nullptr, we can close the connection
+ // If we have no data to send and fileBeingSent is nullptr, we can close the connection next time
if (!cs->persistConnection && nextWrite == nullptr)
{
Close();
@@ -1225,29 +1249,30 @@ bool NetworkTransaction::Send()
// We want to send data from another transaction, so only free up this one
return true;
}
- else
- {
- // The TCP window has been filled up as much as possible, so send it now. There is no need to check
- // the available space in the SNDBUF queue, because we really write only one TCP window at once.
- tcp_sent(cs->pcb, conn_sent);
- err_t result = tcp_write(cs->pcb, sendingWindow(), bytesBeingSent, 0);
- if (result != ERR_OK) // Final arg - 1 means make a copy
- {
- reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: tcp_write returned error code %d, this should never happen!\n", result);
- tcp_abort(cs->pcb);
- cs->pcb = nullptr;
- }
- else
- {
- sendingTransaction = this;
- sendingRetries = 0;
- sendingWindowSize = sentDataOutstanding = bytesBeingSent;
- lastWriteTime = reprap.GetPlatform()->Time();
+ // The TCP window has been filled up as much as possible, so send it now. There is no need to check
+ // the available space in the SNDBUF queue, because we really write only one TCP window at once.
+ err_t result = tcp_write(cs->pcb, sendingWindow(), bytesBeingSent, 0);
+ if (result == ERR_OK)
+ {
+ result = tcp_output(cs->pcb);
+ }
- tcp_output(cs->pcb);
- }
+ if (ERR_IS_FATAL(result))
+ {
+ reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Failed to write data in Send (code %d)\n", result);
+ tcp_abort(cs->pcb);
+ cs->pcb = nullptr;
+ return false;
}
+
+
+ tcp_poll(cs->pcb, conn_poll, TCP_WRITE_TIMEOUT / TCP_SLOW_INTERVAL / TCP_MAX_SEND_RETRIES);
+ tcp_sent(cs->pcb, conn_sent);
+
+ sendingConnection = cs;
+ sendingRetries = 0;
+ sendingWindowSize = sentDataOutstanding = bytesBeingSent;
return false;
}
@@ -1255,21 +1280,21 @@ bool NetworkTransaction::Send()
// the current connection is terminated once everything has been sent.
void NetworkTransaction::Commit(bool keepConnectionAlive)
{
- // If this transaction is already in use for sending, just pretend it was complete
if (status == dataSending)
{
reprap.GetNetwork()->readyTransactions = next;
+ // This transaction is already on the list of sending transactions. Pretend it's already complete
}
else
{
if (LostConnection())
{
+ // Discard transaction if no connection is available
Discard();
-// debugPrintf("Conn lost before send\n");
}
else
{
- // We're actually sending, so this transaction must be complete
+ // We intend to send data, so move this transaction and prepare some values
FreePbuf();
reprap.GetNetwork()->readyTransactions = next;
cs->persistConnection = keepConnectionAlive;
@@ -1286,7 +1311,6 @@ void NetworkTransaction::Commit(bool keepConnectionAlive)
cs->sendingTransaction = this;
NetworkTransaction * volatile * writingTransactions = &reprap.GetNetwork()->writingTransactions;
reprap.GetNetwork()->AppendTransaction(writingTransactions, this);
-// debugPrintf("Transaction queued for writing to network, file=%c, data=%s\n", (f ? 'Y' : 'N'), r->outputBuffer);
}
else
{
@@ -1295,7 +1319,6 @@ void NetworkTransaction::Commit(bool keepConnectionAlive)
mySendingTransaction = mySendingTransaction->nextWrite;
}
mySendingTransaction->nextWrite = this;
-// debugPrintf("Transaction appended to sending RS\n");
}
}
}
@@ -1307,7 +1330,7 @@ void NetworkTransaction::Defer()
FreePbuf();
// Call LWIP task to process tcp_recved(). This will hopefully send an ACK
- while (ethernet_read());
+ ethernet_task();
}
// This method should be called if we don't want to send data to the client and if
@@ -1317,32 +1340,37 @@ void NetworkTransaction::Discard()
// Is this the transaction we should be dealing with?
if (reprap.GetNetwork()->readyTransactions != this)
{
+ // Should never get here
return;
}
reprap.GetNetwork()->readyTransactions = next;
- // Free up some resources...
+ // Free up some resources
FreePbuf();
if (fileBeingSent != nullptr)
{
fileBeingSent->Close();
+ fileBeingSent = nullptr;
}
OutputBuffer::ReleaseAll(sendBuffer);
sendStack->ReleaseAll();
- // Free this transaction again unless it's still referenced
+ // Release this transaction unless it's still in use for sending
if (status != dataSending)
{
NetworkTransaction * volatile * freeTransactions = &reprap.GetNetwork()->freeTransactions;
reprap.GetNetwork()->AppendTransaction(freeTransactions, this);
}
- // Call disconnect events if this transaction indicates a graceful disconnect
+ // Call disconnect event if this transaction indicates a graceful disconnect
if (!LostConnection() && status == disconnected)
{
-// debugPrintf("Network: CloseRequest is closing connection cs=%08x\n", (unsigned int)locCs);
+ if (reprap.Debug(moduleNetwork))
+ {
+ reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Discard() is handling a graceful disconnect for cs=%08x\n", (unsigned int)cs);
+ }
reprap.GetNetwork()->ConnectionClosed(cs, true);
}
}
@@ -1375,7 +1403,6 @@ uint16_t NetworkTransaction::GetLocalPort() const
void NetworkTransaction::Close()
{
tcp_pcb *pcb = cs->pcb;
- tcp_poll(pcb, nullptr, 4);
tcp_recv(pcb, nullptr);
closeRequested = true;
}
@@ -1393,8 +1420,8 @@ void NetworkTransaction::FreePbuf()
if (pb != nullptr)
{
pbuf_free(pb);
- pb = nullptr;
+ pb = readingPb = nullptr;
}
}
-// vim: ts=4:sw=4
+// End
diff --git a/src/Network.h b/src/Network.h
index 64bf5a3b..7c5fab49 100644
--- a/src/Network.h
+++ b/src/Network.h
@@ -15,7 +15,6 @@ Separated out from Platform.h by dc42 and extended by zpl
#include <climits>
#include "lwipopts.h"
-#include "ethernet_sam.h"
#include "OutputMemory.h"
// This class handles the network - typically an Ethernet.
@@ -28,17 +27,19 @@ Separated out from Platform.h by dc42 and extended by zpl
// Currently we set the MSS (in file network/lwipopts.h) to 1432 which matches the value used by most versions of Windows
// and therefore avoids additional memory use and fragmentation.
-const uint8_t NETWORK_TRANSACTION_COUNT = 16; // Number of NetworkTransactions to be used for network IO
-const float TCP_WRITE_TIMEOUT = 8.0; // Seconds to wait for data we have written to be acknowledged
+const size_t NETWORK_TRANSACTION_COUNT = 16; // Number of NetworkTransactions to be used for network IO
+const uint32_t TCP_WRITE_TIMEOUT = 4000; // Miliseconds to wait for data we have written to be acknowledged
+const uint32_t TCP_MAX_SEND_RETRIES = 4; // How many times can we attempt to write data
-const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 }; // Need some sort of default...
+const uint8_t MAC_ADDRESS[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED }; // Need some sort of default...
+const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 };
const uint8_t NET_MASK[4] = { 255, 255, 255, 0 };
const uint8_t GATE_WAY[4] = { 192, 168, 1, 1 };
+const uint16_t DEFAULT_HTTP_PORT = 80;
const uint16_t FTP_PORT = 21;
const uint16_t TELNET_PORT = 23;
-const uint16_t DEFAULT_HTTP_PORT = 80;
/****************************************************************************************************/
@@ -52,14 +53,16 @@ class NetworkTransaction;
struct ConnectionState
{
tcp_pcb *pcb; // Connection PCB
+ uint16_t localPort, remotePort; // Copy of the local and remote ports, because the PCB may be unavailable
+ uint32_t remoteIPAddress; // Same for the remote IP address
NetworkTransaction *sendingTransaction; // NetworkTransaction that is currently sending via this connection
ConnectionState *next; // Next ConnectionState in this list
bool persistConnection; // Do we expect this connection to stay alive?
void Init(tcp_pcb *p);
- uint16_t GetLocalPort() const;
- uint32_t GetRemoteIP() const;
- uint16_t GetRemotePort() const;
+ uint16_t GetLocalPort() const { return localPort; }
+ uint32_t GetRemoteIP() const { return remoteIPAddress; }
+ uint16_t GetRemotePort() const { return remotePort; }
};
// Assign a status to each NetworkTransaction
@@ -82,9 +85,9 @@ class NetworkTransaction
void Set(pbuf *p, ConnectionState* c, TransactionStatus s);
TransactionStatus GetStatus() const { return status; }
- size_t DataLength() const;
+ bool HasMoreDataToRead() const { return readingPb != nullptr; }
bool Read(char& b);
- bool ReadBuffer(char *&buffer, unsigned int &len);
+ bool ReadBuffer(const char *&buffer, unsigned int &len);
void Write(char b);
void Write(const char* s);
void Write(StringRef ref);
@@ -97,9 +100,9 @@ class NetworkTransaction
void SetConnectionLost();
bool LostConnection() const { return cs == nullptr || cs->pcb == nullptr; }
ConnectionState *GetConnection() const { return cs; }
+ uint16_t GetLocalPort() const;
uint32_t GetRemoteIP() const;
uint16_t GetRemotePort() const;
- uint16_t GetLocalPort() const;
void Commit(bool keepConnectionAlive);
void Defer();
@@ -114,7 +117,7 @@ class NetworkTransaction
ConnectionState* cs;
NetworkTransaction* volatile next; // next NetworkTransaction in the list we are in
NetworkTransaction* nextWrite; // next NetworkTransaction queued to write to assigned connection
- pbuf *pb; // linked list of incoming packet buffers
+ pbuf *pb, *readingPb; // received packet queue and a pointer to the pbuf being read from
unsigned int bufferLength; // total length of the packet buffer
unsigned int inputPointer; // amount of data already taken from the first packet buffer
@@ -123,7 +126,6 @@ class NetworkTransaction
FileStore *fileBeingSent;
TransactionStatus status;
- float lastWriteTime;
bool closeRequested;
bool waitingForDataConnection;
};
@@ -135,11 +137,10 @@ class Network
friend class NetworkTransaction;
void ReadPacket();
- void ReceiveInput(pbuf *pb, ConnectionState *cs);
- void SentPacketAcknowledged(ConnectionState *cs, unsigned int len);
+ bool ReceiveInput(pbuf *pb, ConnectionState *cs);
ConnectionState *ConnectionAccepted(tcp_pcb *pcb);
void ConnectionClosed(ConnectionState* cs, bool closeConnection);
- void ConnectionClosedGracefully(ConnectionState *cs);
+ bool ConnectionClosedGracefully(ConnectionState *cs);
NetworkTransaction *GetTransaction(const ConnectionState *cs = nullptr);
void WaitForDataConection();
diff --git a/src/Pins_duet.h b/src/Pins_duet.h
index d4fbacbc..30195b75 100644
--- a/src/Pins_duet.h
+++ b/src/Pins_duet.h
@@ -146,4 +146,8 @@ const size_t NUM_PINS_ALLOWED = 72;
/* pins 64-71 */ 0b00001100 \
}
+// SAM3X Flash locations (may be expanded in the future)
+const uint32_t IAP_FLASH_START = 0x000F0000;
+const uint32_t IAP_FLASH_END = 0x000FFFFF;
+
#endif
diff --git a/src/Platform.cpp b/src/Platform.cpp
index aadb6413..d4160ea7 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -231,9 +231,9 @@ void Platform::Init()
ARRAY_INIT(tempSensePins, TEMP_SENSE_PINS);
ARRAY_INIT(heatOnPins, HEAT_ON_PINS);
- ARRAY_INIT(standbyTemperatures, STANDBY_TEMPERATURES);
- ARRAY_INIT(activeTemperatures, ACTIVE_TEMPERATURES);
ARRAY_INIT(max31855CsPins, MAX31855_CS_PINS);
+
+ configuredHeaters = (BED_HEATER >= 0) ? (1 << BED_HEATER) : 0;
heatSampleTime = HEAT_SAMPLE_TIME;
timeToHot = TIME_TO_HOT;
@@ -694,6 +694,79 @@ void Platform::SetAutoSave(bool enabled)
#endif
}
+void Platform::UpdateFirmware()
+{
+ FileStore *iapFile = GetFileStore(GetSysDir(), IAP_UPDATE_FILE, false);
+ if (iapFile == nullptr)
+ {
+ MessageF(GENERIC_MESSAGE, "Error: Could not open IAP programmer binary \"%s\"\n", IAP_UPDATE_FILE);
+ return;
+ }
+
+ // The machine will be unresponsive for a few seconds, don't risk damaging the heaters...
+ reprap.EmergencyStop();
+
+
+ // Step 1 - Write update binary to Flash
+ char data[IFLASH_PAGE_SIZE];
+ int bytesRead;
+ for(uint32_t flashAddr = IAP_FLASH_START; flashAddr < IAP_FLASH_END; flashAddr += IFLASH_PAGE_SIZE)
+ {
+ bytesRead = iapFile->Read(data, IFLASH_PAGE_SIZE);
+
+ if (bytesRead > 0 && flashAddr + bytesRead < IFLASH_ADDR + IFLASH_SIZE)
+ {
+ // Write one page at a time
+ cpu_irq_disable();
+ flash_unlock(flashAddr, flashAddr + IFLASH_PAGE_SIZE - 1, nullptr, nullptr);
+ flash_write(flashAddr, data, bytesRead, 1);
+ flash_lock(flashAddr, flashAddr + IFLASH_PAGE_SIZE - 1, nullptr, nullptr);
+ cpu_irq_enable();
+
+ // Verify written data
+ if (memcmp(reinterpret_cast<void *>(flashAddr), data, bytesRead) != 0)
+ {
+ Message(GENERIC_MESSAGE, "Error: Verify during Flash write failed!\n");
+ return;
+ }
+ }
+ else
+ {
+ // Cannot read any more or IAP binary exceeds 64KiB limit
+ break;
+ }
+ }
+ iapFile->Close();
+
+ // Step 2 - Reallocate the vector table and program entry point to the new IAP binary
+ // This does essentially what the Atmel AT02333 paper suggests (see 3.2.2 ff)
+
+ // Disable all IRQs
+ cpu_irq_disable();
+ for(size_t i = 0; i < 8; i++)
+ {
+ NVIC->ICER[i] = 0xFFFFFFFF; // Disable IRQs
+ NVIC->ICPR[i] = 0xFFFFFFFF; // Clear pending IRQs
+ }
+
+ // Our SAM3X doesn't support disabling the watchdog, so leave it running.
+ // The IAP binary will kick it as soon as it's started
+
+ // Modify vector table location
+ __DSB();
+ __ISB();
+ SCB->VTOR = ((uint32_t)IAP_FLASH_START & SCB_VTOR_TBLOFF_Msk);
+ __DSB();
+ __ISB();
+
+ // Reset stack pointer, enable IRQs again and start the new IAP binary
+ __set_MSP(*(uint32_t *)IAP_FLASH_START);
+ cpu_irq_enable();
+
+ void *entryPoint = (void *)(*(uint32_t *)(IAP_FLASH_START + 4));
+ goto *entryPoint;
+}
+
// Send the beep command to the aux channel. There is no flow control on this port, so it can't block for long.
void Platform::Beep(int freq, int ms)
{
@@ -934,7 +1007,7 @@ void TC3_Handler()
++numInterruptsExecuted;
lastInterruptTime = Platform::GetInterruptClocks();
#endif
- reprap.Interrupt();
+ reprap.GetMove()->Interrupt();
}
void TC4_Handler()
@@ -1249,7 +1322,7 @@ float Platform::GetTemperature(size_t heater, TempError* err) const
// See if we need to turn on the hot end fan
bool Platform::AnyHeaterHot(uint16_t heaters, float t) const
{
- for (size_t h = 0; h < reprap.GetHeatersInUse(); ++h)
+ for (size_t h = 0; h < reprap.GetToolHeatersInUse(); ++h)
{
if (((1 << h) & heaters) != 0)
{
@@ -1260,6 +1333,17 @@ bool Platform::AnyHeaterHot(uint16_t heaters, float t) const
}
}
}
+
+ // Check the bed heater too, in case the user wants a fan on when the bed is hot
+ int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
+ if (bedHeater >= 0 && ((1 << (unsigned int)bedHeater) & heaters) != 0)
+ {
+ const float ht = GetTemperature(bedHeater);
+ if (ht >= t || ht < BAD_LOW_TEMPERATURE)
+ {
+ return true;
+ }
+ }
return false;
}
@@ -1297,6 +1381,34 @@ void Platform::SetHeaterPwm(size_t heater, uint8_t power)
}
}
+void Platform::UpdateConfiguredHeaters()
+{
+ configuredHeaters = 0;
+
+ // Check bed heater
+ const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
+ if (bedHeater >= 0)
+ {
+ configuredHeaters |= (1 << bedHeater);
+ }
+
+ // Check chamber heater
+ const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
+ if (chamberHeater >= 0)
+ {
+ configuredHeaters |= (1 << chamberHeater);
+ }
+
+ // Check tool heaters
+ for(size_t heater = 0; heater < HEATERS; heater++)
+ {
+ if (reprap.IsHeaterAssignedToTool(heater))
+ {
+ configuredHeaters |= (1 << heater);
+ }
+ }
+}
+
EndStopHit Platform::Stopped(size_t drive) const
{
if (endStopType[drive] == EndStopType::noEndStop)
@@ -2277,7 +2389,7 @@ void Platform::Tick()
ThermistorAveragingFilter& currentFilter = const_cast<ThermistorAveragingFilter&>(thermistorFilters[currentHeater]);
currentFilter.ProcessReading(GetAdcReading(thermistorAdcChannels[heaterTempChannels[currentHeater]]));
StartAdcConversion(zProbeAdcChannel);
- if (currentFilter.IsValid())
+ if (currentFilter.IsValid() && (configuredHeaters & (1 << currentHeater)) != 0)
{
uint32_t sum = currentFilter.GetSum();
if (sum < thermistorOverheatSums[currentHeater] || sum >= AD_DISCONNECTED_REAL * THERMISTOR_AVERAGE_READINGS)
diff --git a/src/Platform.h b/src/Platform.h
index 3f2f9696..484cc2ea 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -171,8 +171,6 @@ const size_t MAX_FILES = 10; // Must be large enough to handle the max numbe
const size_t FILE_BUFFER_SIZE = 256;
-const uint8_t MAC_ADDRESS[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED };
-
/****************************************************************************************************/
enum class BoardType : uint8_t
@@ -574,6 +572,7 @@ public:
float GetTemperatureLimit() const { return temperatureLimit; }
static const char* TempErrorStr(TempError err);
static bool TempErrorPermanent(TempError err);
+ void UpdateConfiguredHeaters();
// Fans
@@ -593,9 +592,10 @@ public:
void ResetNvData();
void ReadNvData();
void WriteNvData();
-
void SetAutoSave(bool enabled);
+ void UpdateFirmware();
+
// AUX device
void Beep(int freq, int ms);
@@ -757,9 +757,8 @@ private:
Pin heatOnPins[HEATERS];
MAX31855 Max31855Devices[MAX31855_DEVICES];
Pin max31855CsPins[MAX31855_DEVICES];
+ uint32_t configuredHeaters; // Bitmask of all heaters in use
float heatSampleTime;
- float standbyTemperatures[HEATERS];
- float activeTemperatures[HEATERS];
float timeToHot;
float temperatureLimit;
diff --git a/src/PrintMonitor.cpp b/src/PrintMonitor.cpp
index 213b6742..5f0005cd 100644
--- a/src/PrintMonitor.cpp
+++ b/src/PrintMonitor.cpp
@@ -19,10 +19,10 @@ Licence: GPL
#include "RepRapFirmware.h"
-PrintMonitor::PrintMonitor(Platform *p, GCodes *gc) : platform(p), gCodes(gc), isPrinting(false), isHeating(false),
- printStartTime(0), pauseStartTime(0.0), totalPauseTime(0.0), currentLayer(0), warmUpDuration(0.0),
+PrintMonitor::PrintMonitor(Platform *p, GCodes *gc) : platform(p), gCodes(gc), isPrinting(false),
+ printStartTime(0), pauseStartTime(0.0), totalPauseTime(0.0), heatingUp(false), currentLayer(0), warmUpDuration(0.0),
firstLayerDuration(0.0), firstLayerFilament(0.0), firstLayerProgress(0.0), lastLayerChangeTime(0.0),
- lastLayerFilament(0.0), numLayerSamples(0), layerEstimatedTimeLeft(0.0), parseState(notParsing),
+ lastLayerFilament(0.0), lastLayerZ(0.0), numLayerSamples(0), layerEstimatedTimeLeft(0.0), parseState(notParsing),
fileBeingParsed(nullptr), fileOverlapLength(0), printingFileParsed(false), accumulatedParseTime(0.0),
accumulatedReadTime(0.0)
{
@@ -32,12 +32,12 @@ PrintMonitor::PrintMonitor(Platform *p, GCodes *gc) : platform(p), gCodes(gc), i
void PrintMonitor::Init()
{
longWait = platform->Time();
+ lastUpdateTime = millis();
}
void PrintMonitor::Spin()
{
- // We might have started a file print while another G-Code file is being parsed.
- // So we need to start this process once the other file has been processed.
+ // File information about the file being printed must be available before layer estimations can be made
if (filenameBeingPrinted[0] != 0 && !printingFileParsed)
{
printingFileParsed = GetFileInfo(platform->GetGCodeDir(), filenameBeingPrinted, printingFileInfo);
@@ -48,8 +48,8 @@ void PrintMonitor::Spin()
}
}
- // Don't update the print time estimations if there is no file info or if the print has been paused
- if (gCodes->IsPausing() || gCodes->IsPaused() || gCodes->IsResuming())
+ // Don't do any updates if the print has been paused
+ if (!gCodes->IsRunning())
{
if (pauseStartTime == 0.0)
{
@@ -59,14 +59,15 @@ void PrintMonitor::Spin()
return;
}
- // Otherwise try to update them
+ // Otherwise collect some stats after a certain period of time
+ uint32_t now = millis();
if (IsPrinting()
#if SUPPORT_ROLAND
- && !reprap.GetRoland()->Active()
+ && !reprap.GetRoland()->Active()
#endif
- )
+ && now - lastUpdateTime > PRINTMONITOR_UPDATE_INTERVAL)
{
- // We might need to adjust the actual print time if it was paused before
+ // Adjust the actual print time if the print was paused before
if (pauseStartTime != 0.0)
{
totalPauseTime += platform->Time() - pauseStartTime;
@@ -74,73 +75,88 @@ void PrintMonitor::Spin()
}
// Have we just started a print? See if we're heating up
- if (warmUpDuration == 0.0)
+ if (currentLayer == 0)
{
- // Check if at least one nozzle heater is active and set
- bool heatersAtHighTemperature = false;
- for(size_t heater = E0_HEATER; heater < HEATERS; heater++)
+ // Check if there are any active heaters
+ bool nozzleAtHighTemperature = false;
+ for(int heater = 0; heater < HEATERS; heater++)
{
- if (reprap.GetHeat()->GetStatus(heater) == Heat::HS_active &&
- reprap.GetHeat()->GetActiveTemperature(heater) > TEMPERATURE_LOW_SO_DONT_CARE)
+ if (reprap.GetHeat()->GetStatus(heater) == Heat::HS_active && reprap.GetHeat()->GetActiveTemperature(heater) > TEMPERATURE_LOW_SO_DONT_CARE)
{
- isHeating = true;
- if (reprap.GetHeat()->HeaterAtSetTemperature(heater))
+ heatingUp = true;
+
+ // Check if this heater is assigned to a tool and if it has reached its set temperature yet
+ if (reprap.IsHeaterAssignedToTool(heater))
{
- heatersAtHighTemperature = true;
- isHeating = false;
- break;
+ if (!reprap.GetHeat()->HeaterAtSetTemperature(heater))
+ {
+ nozzleAtHighTemperature = false;
+ break;
+ }
+ nozzleAtHighTemperature = true;
}
}
}
- // Yes - do we have live momement?
- if (heatersAtHighTemperature && !reprap.GetMove()->NoLiveMovement())
+ // Yes - do we have live movement?
+ if (nozzleAtHighTemperature && !reprap.GetMove()->NoLiveMovement())
{
// Yes - we're actually starting the print
WarmUpComplete();
+ currentLayer = 1;
}
}
- // Print is in progress...
- else if (currentLayer > 0 && !gCodes->DoingFileMacro())
+ // Print is in progress and filament is being extruded
+ else if (!gCodes->DoingFileMacro() && reprap.GetMove()->IsExtruding())
{
- float liveCoords[DRIVES + 1];
- reprap.GetMove()->LiveCoordinates(liveCoords);
+ float liveCoordinates[DRIVES];
+ reprap.GetMove()->LiveCoordinates(liveCoordinates);
// See if we need to determine the first layer height (usually smaller than the nozzle diameter)
if (printingFileInfo.firstLayerHeight == 0.0)
{
- if (liveCoords[Z_AXIS] < platform->GetNozzleDiameter() * 1.5)
+ if (liveCoordinates[Z_AXIS] < platform->GetNozzleDiameter() * 1.5)
{
// This shouldn't be needed because we parse the first layer height anyway, but it won't harm
- printingFileInfo.firstLayerHeight = liveCoords[Z_AXIS];
+ printingFileInfo.firstLayerHeight = liveCoordinates[Z_AXIS];
}
}
- // Then check if we've finished the first layer
- else if (firstLayerDuration == 0.0)
+ // Check if we've finished the first layer
+ else if (currentLayer == 1)
{
- if (HeightMatches(liveCoords[Z_AXIS], printingFileInfo.firstLayerHeight + printingFileInfo.layerHeight))
+ if (liveCoordinates[Z_AXIS] > printingFileInfo.firstLayerHeight + LAYER_HEIGHT_TOLERANCE)
{
- // First layer is complete
FirstLayerComplete();
+ currentLayer++;
+
+ lastLayerZ = liveCoordinates[Z_AXIS];
+ lastLayerChangeTime = GetPrintDuration();
}
}
- // We have enough values to estimate the following layer heights
- else if (printingFileInfo.objectHeight > 0.0)
+ // Check for following layer changes
+ else if (liveCoordinates[Z_AXIS] > lastLayerZ + LAYER_HEIGHT_TOLERANCE)
{
- // Check for layer change
- float nextLayerZ = printingFileInfo.firstLayerHeight + currentLayer * printingFileInfo.layerHeight;
- if (HeightMatches(liveCoords[Z_AXIS], nextLayerZ))
- {
- // A new layer is now being printed
- LayerComplete();
- }
+ LayerComplete();
+ currentLayer++;
+
+ lastLayerZ = liveCoordinates[Z_AXIS];
+ lastLayerChangeTime = GetPrintDuration();
}
}
+ lastUpdateTime = now;
}
-
platform->ClassReport(longWait);
}
+float PrintMonitor::GetWarmUpDuration() const
+{
+ if (currentLayer > 0)
+ {
+ return warmUpDuration;
+ }
+ return heatingUp ? GetPrintDuration() : 0.0;
+}
+
// Notifies this class that a file has been set for printing
void PrintMonitor::StartingPrint(const char* filename)
{
@@ -159,105 +175,98 @@ void PrintMonitor::StartedPrint()
// This is called as soon as the heaters are at temperature and the actual print has started
void PrintMonitor::WarmUpComplete()
{
+ heatingUp = false;
warmUpDuration = GetPrintDuration();
- if (printingFileInfo.layerHeight > 0.0) {
- currentLayer = 1;
- }
}
// Called when the first layer has been finished
void PrintMonitor::FirstLayerComplete()
{
- firstLayerFilament = gCodes->GetTotalRawExtrusion();
+ firstLayerFilament = RawFilamentExtruded();
firstLayerDuration = GetPrintDuration() - warmUpDuration;
firstLayerProgress = gCodes->FractionOfFilePrinted();
+
+ // Update layer-based estimation time (if the object and layer heights are known)
+ // This won't be very accurate, but at least something can be sent the web interface and to PanelDue
+ if (printingFileInfo.objectHeight > 0.0 && printingFileInfo.layerHeight > 0.0)
+ {
+ unsigned int layersToPrint = round((printingFileInfo.objectHeight - printingFileInfo.firstLayerHeight) / printingFileInfo.layerHeight) + 1;
+ layerEstimatedTimeLeft = firstLayerDuration * FIRST_LAYER_SPEED_FACTOR * (layersToPrint - 1);
+ }
}
-// This is called whenever another layer has been finished
+// This is called whenever a layer greater than 2 has been finished
void PrintMonitor::LayerComplete()
{
- // Use untainted extruder positions for filament-based estimation
- const float extrRawTotal = gCodes->GetTotalRawExtrusion();
-
// Record a new set of layer, filament and file stats
- if (currentLayer > 1)
+ const float extrRawTotal = RawFilamentExtruded();
+ if (numLayerSamples < MAX_LAYER_SAMPLES)
{
- // Record a new set
- if (numLayerSamples < MAX_LAYER_SAMPLES)
+ if (numLayerSamples == 0)
{
- if (numLayerSamples == 0)
- {
- filamentUsagePerLayer[numLayerSamples] = extrRawTotal - firstLayerFilament;
- layerDurations[numLayerSamples] = GetPrintDuration() - warmUpDuration;
- }
- else
- {
- filamentUsagePerLayer[numLayerSamples] = extrRawTotal - lastLayerFilament;
- layerDurations[numLayerSamples] = GetPrintDuration() - lastLayerChangeTime;
- }
- fileProgressPerLayer[numLayerSamples] = gCodes->FractionOfFilePrinted();
- numLayerSamples++;
+ filamentUsagePerLayer[numLayerSamples] = extrRawTotal - firstLayerFilament;
+ layerDurations[numLayerSamples] = GetPrintDuration() - warmUpDuration;
}
else
{
- for(size_t i = 1; i < MAX_LAYER_SAMPLES; i++)
- {
- layerDurations[i - 1] = layerDurations[i];
- filamentUsagePerLayer[i - 1] = filamentUsagePerLayer[i];
- fileProgressPerLayer[i - 1] = fileProgressPerLayer[i];
- }
-
- layerDurations[MAX_LAYER_SAMPLES - 1] = GetPrintDuration() - lastLayerChangeTime;
- filamentUsagePerLayer[MAX_LAYER_SAMPLES - 1] = extrRawTotal - lastLayerFilament;
- fileProgressPerLayer[MAX_LAYER_SAMPLES - 1] = gCodes->FractionOfFilePrinted();
+ filamentUsagePerLayer[numLayerSamples] = extrRawTotal - lastLayerFilament;
+ layerDurations[numLayerSamples] = GetPrintDuration() - lastLayerChangeTime;
}
+ fileProgressPerLayer[numLayerSamples] = gCodes->FractionOfFilePrinted();
+ numLayerSamples++;
}
+ else
+ {
+ for(size_t i = 1; i < MAX_LAYER_SAMPLES; i++)
+ {
+ layerDurations[i - 1] = layerDurations[i];
+ filamentUsagePerLayer[i - 1] = filamentUsagePerLayer[i];
+ fileProgressPerLayer[i - 1] = fileProgressPerLayer[i];
+ }
- // Update layer-based estimation times
- unsigned int remainingLayers;
- remainingLayers = round((printingFileInfo.objectHeight - printingFileInfo.firstLayerHeight) / printingFileInfo.layerHeight) + 1;
- remainingLayers -= currentLayer;
+ layerDurations[MAX_LAYER_SAMPLES - 1] = GetPrintDuration() - lastLayerChangeTime;
+ filamentUsagePerLayer[MAX_LAYER_SAMPLES - 1] = extrRawTotal - lastLayerFilament;
+ fileProgressPerLayer[MAX_LAYER_SAMPLES - 1] = gCodes->FractionOfFilePrinted();
+ }
+ lastLayerFilament = extrRawTotal;
- float avgLayerTime, avgLayerDelta = 0.0;
- if (numLayerSamples)
+ // Update layer-based estimation time (if the object and layer heights are known)
+ if (printingFileInfo.objectHeight > 0.0 && printingFileInfo.layerHeight > 0.0)
{
- avgLayerTime = 0.0;
+ // Calculate the average layer time and include the first layer if possible
+ float avgLayerTime = (numLayerSamples < MAX_LAYER_SAMPLES)
+ ? firstLayerDuration * FIRST_LAYER_SPEED_FACTOR
+ : 0.0;
for(size_t layer = 0; layer < numLayerSamples; layer++)
{
avgLayerTime += layerDurations[layer];
- if (layer)
- {
- avgLayerDelta += layerDurations[layer] - layerDurations[layer - 1];
- }
}
- avgLayerTime /= numLayerSamples;
- avgLayerDelta /= numLayerSamples;
- }
- else
- {
- avgLayerTime = firstLayerDuration * FIRST_LAYER_SPEED_FACTOR;
- }
+ avgLayerTime /= (numLayerSamples < MAX_LAYER_SAMPLES) ? numLayerSamples + 1 : numLayerSamples;
- layerEstimatedTimeLeft = (avgLayerTime * remainingLayers) - (avgLayerDelta * remainingLayers);
- if (layerEstimatedTimeLeft < 0.0)
- {
- layerEstimatedTimeLeft = avgLayerTime * remainingLayers;
+ // Estimate the layer-based time left
+ unsigned int totalLayers;
+ totalLayers = round((printingFileInfo.objectHeight - printingFileInfo.firstLayerHeight) / printingFileInfo.layerHeight) + 1;
+ if (currentLayer < totalLayers)
+ {
+ // Current layer is within reasonable boundaries, so an estimation can be made
+ layerEstimatedTimeLeft = avgLayerTime * (totalLayers - currentLayer);
+ }
+ else
+ {
+ // Current layer is higher than the maximum number of layers. Assume the print has almost finished
+ layerEstimatedTimeLeft = 0.1;
+ }
}
-
- // Set new layer values
- currentLayer++;
- lastLayerChangeTime = GetPrintDuration();
- lastLayerFilament = extrRawTotal;
}
void PrintMonitor::StoppedPrint()
{
- isPrinting = printingFileParsed = false;
+ isPrinting = heatingUp = printingFileParsed = false;
currentLayer = numLayerSamples = 0;
pauseStartTime = totalPauseTime = 0.0;
firstLayerDuration = firstLayerFilament = firstLayerProgress = 0.0;
layerEstimatedTimeLeft = printStartTime = warmUpDuration = 0.0;
- lastLayerChangeTime = lastLayerFilament = 0.0;
+ lastLayerChangeTime = lastLayerFilament = lastLayerZ = 0.0;
}
bool PrintMonitor::GetFileInfo(const char *directory, const char *fileName, GCodeFileInfo& info)
@@ -700,61 +709,75 @@ void PrintMonitor::StopParsing(const char *filename)
// Estimate the print time left in seconds on a preset estimation method
float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
{
- // We can't provide an estimation if we're not printing (yet)
- if (!printingFileParsed && warmUpDuration == 0.0)
+ // We can't provide an estimation if we don't have any information about the file
+ if (!printingFileParsed)
{
return 0.0;
}
// How long have we been printing continuously?
float realPrintDuration = GetPrintDuration() - warmUpDuration;
- if (numLayerSamples != 0)
- {
- // Take into account the first layer time only if we haven't got any other samples
- realPrintDuration -= firstLayerDuration;
- }
- // Actual estimations
switch (method)
{
case fileBased:
{
- // Provide rough estimation only if we haven't collected at least 2 layer samples
+ // Can we provide an estimation at all?
const float fractionPrinted = gCodes->FractionOfFilePrinted();
- if (numLayerSamples < 2 || !printingFileParsed || printingFileInfo.objectHeight == 0.0)
+ if (fractionPrinted < ESTIMATION_MIN_FILE_USAGE || heatingUp)
{
- if (fractionPrinted < 0.01)
- {
- break;
- }
- return realPrintDuration * (1.0 / fractionPrinted) - realPrintDuration;
+ // No, we haven't printed enough of the file yet. We can't provide an estimation at this moment
+ return 0.0;
+ }
+ if (fractionPrinted == 1.0)
+ {
+ // No, but the file has been processed entirely. It won't take long until the print finishes
+ return 0.1;
}
- // Work out how much progress we made in the layers we have data for, and how long it took.
- // Can't use the first layer sample in the table because we don't know the fraction printed at the start.
- float duration = 0.0;
- for (size_t layer = 1; layer < numLayerSamples; layer++)
+ // See how long it takes per progress
+ float duration, fractionPrintedInLayers;
+ if (numLayerSamples == 0)
{
- duration += layerDurations[layer];
+ duration = firstLayerDuration;
+ fractionPrintedInLayers = firstLayerProgress;
}
- const float fractionPrintedInLayers = fileProgressPerLayer[numLayerSamples - 1] - fileProgressPerLayer[0];
- if (fractionPrintedInLayers >= 0.01)
+ else if (numLayerSamples == 1)
{
- return duration * (1.0 - fractionPrinted)/fractionPrintedInLayers;
+ duration = layerDurations[0];
+ fractionPrintedInLayers = fileProgressPerLayer[0] - firstLayerProgress;
}
+ else if (numLayerSamples > 1)
+ {
+ duration = 0.0;
+ for(size_t sample = 1; sample < numLayerSamples; sample++)
+ {
+ duration += layerDurations[sample];
+ }
+ fractionPrintedInLayers = fileProgressPerLayer[numLayerSamples - 1] - fileProgressPerLayer[0];
+ }
+
+ // Can we use these values?
+ if (fractionPrintedInLayers < ESTIMATION_MIN_FILE_USAGE)
+ {
+ // No - only provide a rough estimation
+ return max<float>(realPrintDuration * (1.0 / fractionPrinted) - realPrintDuration, 0.1);
+ }
+
+ // Yes...
+ return max<float>(duration * (1.0 - fractionPrinted) / fractionPrintedInLayers, 0.1);
}
- break;
case filamentBased:
{
// Need some file information, otherwise this method won't work
- if (!printingFileParsed || printingFileInfo.numFilaments == 0
+ if (currentLayer == 0 || printingFileInfo.numFilaments == 0
#if SUPPORT_ROLAND
|| reprap.GetRoland()->Active()
#endif
)
{
- break;
+ return 0.0;
}
// Sum up the filament usage and the filament needed
@@ -768,41 +791,33 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
// If we have a reasonable amount of filament extruded, calculate estimated times left
if (totalFilamentNeeded > 0.0 && extrRawTotal > totalFilamentNeeded * ESTIMATION_MIN_FILAMENT_USAGE)
{
- if (firstLayerFilament == 0.0)
- {
- return realPrintDuration * (totalFilamentNeeded - extrRawTotal) / extrRawTotal;
- }
+ // Do we have more total filament extruded than reported by the file
if (extrRawTotal >= totalFilamentNeeded)
{
- break; // Avoid division by zero, else the web interface will report AJAX errors
+ // Yes - assume the print has almost finished
+ return 0.1;
}
- float filamentRate;
- if (numLayerSamples != 0)
+ // Get filament usage per layer
+ float filamentRate = 0.0;
+ if (numLayerSamples > 0)
{
- filamentRate = 0.0;
- size_t numSamples = 0;
- for (size_t i = 0; i < numLayerSamples; i++)
- {
- if (layerDurations[i] > 0.0)
- {
- filamentRate += filamentUsagePerLayer[i] / layerDurations[i];
- ++numSamples;
- }
- }
- if (numSamples == 0)
+ for(size_t i = 0; i < numLayerSamples; i++)
{
- break;
+ filamentRate += filamentUsagePerLayer[i] / layerDurations[i];
}
- filamentRate /= numSamples;
+ filamentRate /= numLayerSamples;
}
else if (firstLayerDuration > 0.0)
{
filamentRate = firstLayerFilament / firstLayerDuration;
}
- else
+
+ // Can we provide a good estimation?
+ if (filamentRate == 0.0)
{
- break;
+ // No - calculate time left based on the filament we have extruded so far
+ return realPrintDuration * (totalFilamentNeeded - extrRawTotal) / extrRawTotal;
}
return (totalFilamentNeeded - extrRawTotal) / filamentRate;
@@ -811,13 +826,11 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
}
case layerBased:
+ // Layer-based estimations are made after each layer change, only reflect this value
if (layerEstimatedTimeLeft > 0.0)
{
float timeLeft = layerEstimatedTimeLeft - (GetPrintDuration() - lastLayerChangeTime);
- if (timeLeft > 0.0)
- {
- return timeLeft;
- }
+ return (timeLeft > 0.0) ? timeLeft : 0.1;
}
break;
}
@@ -833,9 +846,10 @@ bool PrintMonitor::FindFirstLayerHeight(const char* buf, size_t len, float& heig
// Don't start if the buffer is not big enough
return false;
}
+ height = 0.0;
//debugPrintf("Scanning %u bytes starting %.100s\n", len, buf);
- bool inComment = false, inRelativeMode = false;
+ bool inComment = false, inRelativeMode = false, foundHeight = false;
for(size_t i = 0; i < len - 4; i++)
{
if (buf[i] == ';')
@@ -861,7 +875,7 @@ bool PrintMonitor::FindFirstLayerHeight(const char* buf, size_t len, float& heig
{
inRelativeMode = true;
}
- // Look for first "G0/G1 ... Z#HEIGHT#" command
+ // Look for "G0/G1 ... Z#HEIGHT#" command
else if ((buf[i + 1] == '0' || buf[i + 1] == '1') && buf[i + 2] == ' ')
{
for(i += 3; i < len - 4; i++)
@@ -870,10 +884,11 @@ bool PrintMonitor::FindFirstLayerHeight(const char* buf, size_t len, float& heig
{
//debugPrintf("Found at offset %u text: %.100s\n", i, &buf[i + 1]);
float flHeight = strtod(&buf[i + 1], nullptr);
- if (flHeight <= platform->GetNozzleDiameter() * 3.0)
+ if ((height == 0.0 || flHeight < height) && (flHeight <= platform->GetNozzleDiameter() * 3.0))
{
- height = flHeight; // Only report first Z height if it's somewhat reasonable
- return true;
+ height = flHeight; // Only report first Z height if it's somewhat reasonable
+ foundHeight = true;
+ // NB: Don't stop here, because some slicers generate two Z moves at the beginning
}
break;
}
@@ -886,15 +901,28 @@ bool PrintMonitor::FindFirstLayerHeight(const char* buf, size_t len, float& heig
}
}
}
- return false;
+ return foundHeight;
+}
+
+// Get the sum of extruded filament (in mm)
+float PrintMonitor::RawFilamentExtruded() const
+{
+ return reprap.GetGCodes()->GetTotalRawExtrusion();
}
+
// Scan the buffer in reverse for a G1 Zxxx command. The buffer is null-terminated.
bool PrintMonitor::FindHeight(const char* buf, size_t len, float& height) const
{
-//debugPrintf("Scanning %u bytes starting %.100s\n", len, buf);
+ if (len < 5)
+ {
+ // Don't start if the buffer is not big enough
+ return false;
+ }
+
+ //debugPrintf("Scanning %u bytes starting %.100s\n", len, buf);
bool inComment, inRelativeMode = false;
unsigned int zPos;
- for(int i = (int)len - 5; i > 0; i--)
+ for(size_t i = len - 5; i > 0; i--)
{
if (inRelativeMode)
{
@@ -934,33 +962,41 @@ bool PrintMonitor::FindHeight(const char* buf, size_t len, float& height) const
// Find 'Z' position and grab that value
zPos = 0;
- for(int j = i + 3; j < (int)len - 2; j++)
+ for(size_t j = i + 3; j < len - 2; j++)
{
char c = buf[j];
- if (c < ' ')
+ if (c == ';')
{
- // Skip all whitespaces...
- while (j < (int)len - 2 && c <= ' ')
- {
- c = buf[++j];
- }
- // ...to make sure ";End" doesn't follow G0 .. Z#HEIGHT#
- if (zPos != 0 && (buf[j] != ';' || buf[j + 1] != 'E'))
- {
- //debugPrintf("Found at offset %u text: %.100s\n", zPos, &buf[zPos + 1]);
- height = strtod(&buf[zPos + 1], nullptr);
- return true;
- }
- break;
+ inComment = true;
}
- else if (c == ';')
+ else if (c == 'Z' && !inComment)
{
- // Ignore comments
- break;
+ zPos = j;
}
- else if (c == 'Z')
+ else if (c == '\n')
{
- zPos = j;
+ if (zPos != 0)
+ {
+ // Check special case of this code ending with ";E" - ignore such codes
+ j = zPos;
+ while (j < len - 2 && c != '\n')
+ {
+ c = buf[++j];
+ if (c == ';' && buf[j + 1] == 'E')
+ {
+ zPos = 0;
+ break;
+ }
+ }
+
+ if (zPos != 0)
+ {
+ // Z position is valid - read it
+ height = strtod(&buf[zPos + 1], nullptr);
+ return true;
+ }
+ }
+ break;
}
}
}
@@ -969,7 +1005,7 @@ bool PrintMonitor::FindHeight(const char* buf, size_t len, float& height) const
else
{
const char *kisslicerHeightString = "; END_LAYER_OBJECT z=";
- if (i < (int)len - 32 && StringStartsWith(buf + i, kisslicerHeightString))
+ if (i + 32 < len && StringStartsWith(buf + i, kisslicerHeightString))
{
height = strtod(buf + i + strlen(kisslicerHeightString), nullptr);
return true;
@@ -1109,7 +1145,7 @@ float PrintMonitor::GetPrintDuration() const
float printDuration = platform->Time() - printStartTime - totalPauseTime;
if (pauseStartTime != 0.0)
{
- // Take into account how long the machine has been paused for the time estimation
+ // Take into account how long the machine has been paused
printDuration -= platform->Time() - pauseStartTime;
}
return printDuration;
diff --git a/src/PrintMonitor.h b/src/PrintMonitor.h
index 7eade589..ab96dcdb 100644
--- a/src/PrintMonitor.h
+++ b/src/PrintMonitor.h
@@ -25,12 +25,15 @@ const FilePosition GCODE_FOOTER_SIZE = 128000uL; // How many bytes to read from
const size_t GCODE_READ_SIZE = 1024; // How many bytes to read in one go in GetFileInfo() (should be a multiple of 4 for read efficiency)
const size_t GCODE_OVERLAP_SIZE = 100; // Size of the overlapping buffer for searching (should be a multple of 4 as well)
-const float LAYER_HEIGHT_TOLERANCE = 0.025; // Tolerance for comparing two Z heights (in mm)
+const float LAYER_HEIGHT_TOLERANCE = 0.015; // Tolerance for comparing two Z heights (in mm)
const size_t MAX_LAYER_SAMPLES = 5; // Number of layer samples for end-time estimation (except for first layer)
const float ESTIMATION_MIN_FILAMENT_USAGE = 0.01; // Minimum per cent of filament to be printed before the filament-based estimation returns values
+const float ESTIMATION_MIN_FILE_USAGE = 0.001; // Minium per cent of the file to be processed before any file-based estimations are made
const float FIRST_LAYER_SPEED_FACTOR = 0.25; // First layer speed factor compared to other layers (only for layer-based estimation)
+const uint32_t PRINTMONITOR_UPDATE_INTERVAL = 200; // Update interval in milliseconds
+
enum PrintEstimationMethod
{
filamentBased,
@@ -90,20 +93,22 @@ class PrintMonitor
Platform *platform;
GCodes *gCodes;
float longWait;
+ uint32_t lastUpdateTime;
// Information/Events concerning the file being printed
void WarmUpComplete();
void FirstLayerComplete();
void LayerComplete();
- bool isPrinting, isHeating;
+ bool isPrinting;
float printStartTime;
float pauseStartTime, totalPauseTime;
+ bool heatingUp;
unsigned int currentLayer;
float warmUpDuration, firstLayerDuration;
float firstLayerFilament, firstLayerProgress;
- float lastLayerChangeTime, lastLayerFilament;
+ float lastLayerChangeTime, lastLayerFilament, lastLayerZ;
unsigned int numLayerSamples;
float layerDurations[MAX_LAYER_SAMPLES];
@@ -111,6 +116,8 @@ class PrintMonitor
float fileProgressPerLayer[MAX_LAYER_SAMPLES];
float layerEstimatedTimeLeft;
+ float RawFilamentExtruded() const;
+
// We parse G-Code files in multiple stages. These variables hold the required information
FileParseState parseState;
char filenameBeingParsed[FILENAME_LENGTH];
@@ -131,20 +138,14 @@ class PrintMonitor
unsigned int FindFilamentUsed(const char* buf, size_t len, float *filamentUsed, unsigned int maxFilaments) const;
float accumulatedParseTime, accumulatedReadTime;
-
- // Helper methods
- bool HeightMatches(float actual, float expected) const;
};
inline bool PrintMonitor::IsPrinting() const { return isPrinting; }
inline unsigned int PrintMonitor::GetCurrentLayer() const { return currentLayer; }
inline float PrintMonitor::GetCurrentLayerTime() const { return (lastLayerChangeTime > 0.0) ? (GetPrintDuration() - lastLayerChangeTime) : 0.0; }
-inline float PrintMonitor::GetWarmUpDuration() const { return (warmUpDuration > 0.0) ? warmUpDuration : (isHeating ? GetPrintDuration() : 0.0); }
-inline float PrintMonitor::GetFirstLayerDuration() const { return (firstLayerDuration > 0.0) ? firstLayerDuration : ((warmUpDuration > 0) ? GetPrintDuration() - warmUpDuration : 0.0); }
+inline float PrintMonitor::GetFirstLayerDuration() const { return (firstLayerDuration > 0.0) ? firstLayerDuration : ((currentLayer > 0) ? GetPrintDuration() - warmUpDuration : 0.0); }
inline float PrintMonitor::GetFirstLayerHeight() const { return printingFileParsed ? printingFileInfo.firstLayerHeight : 0.0; }
-inline bool PrintMonitor::HeightMatches(float actual, float expected) const { return (expected - LAYER_HEIGHT_TOLERANCE < actual) && (expected + LAYER_HEIGHT_TOLERANCE > actual); }
-
#endif /* PRINTMONITOR_H */
// vim: ts=4:sw=4
diff --git a/src/Reprap.cpp b/src/Reprap.cpp
index 0144a238..6b9adf40 100644
--- a/src/Reprap.cpp
+++ b/src/Reprap.cpp
@@ -4,8 +4,9 @@
// Do nothing more in the constructor; put what you want in RepRap:Init()
-RepRap::RepRap() : lastToolWarningTime(0.0), ticksInSpinState(0), spinningModule(noModule), debug(0),
- stopped(false), active(false), resetting(false)
+RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastToolWarningTime(0.0), activeExtruders(0),
+ activeToolHeaters(0), ticksInSpinState(0), spinningModule(noModule), debug(0), stopped(false),
+ active(false), resetting(false), processingConfig(true), beepFrequency(0), beepDuration(0)
{
OutputBuffer::Init();
platform = new Platform();
@@ -21,24 +22,13 @@ RepRap::RepRap() : lastToolWarningTime(0.0), ticksInSpinState(0), spinningModule
printMonitor = new PrintMonitor(platform, gCodes);
- toolList = nullptr;
-}
-
-void RepRap::Init()
-{
- debug = 0;
-
- // chrishamm thinks it's a bad idea to count the bed as an active heater...
- activeExtruders = activeHeaters = 0;
-
SetPassword(DEFAULT_PASSWORD);
SetName(DEFAULT_NAME);
-
- beepFrequency = beepDuration = 0;
message[0] = 0;
+}
- processingConfig = true;
-
+void RepRap::Init()
+{
// All of the following init functions must execute reasonably quickly before the watchdog times us out
platform->Init();
gCodes->Init();
@@ -50,26 +40,22 @@ void RepRap::Init()
roland->Init();
#endif
printMonitor->Init();
- currentTool = nullptr;
- message[0] = 0;
- Platform::EnableWatchdog();
+ Platform::EnableWatchdog(); // do this after all init calls are made
active = true; // must do this before we start the network, else the watchdog may time out
platform->MessageF(HOST_MESSAGE, "%s Version %s dated %s\n", NAME, VERSION, DATE);
+ // Run the configuration file
const char *configFile = platform->GetConfigFile();
- FileStore *startup = platform->GetFileStore(platform->GetSysDir(), configFile, false);
-
platform->Message(HOST_MESSAGE, "\nExecuting ");
- if (startup != nullptr)
+ if (platform->GetMassStorage()->FileExists(platform->GetSysDir(), configFile))
{
- startup->Close();
- platform->MessageF(HOST_MESSAGE, "%s... ", configFile);
+ platform->MessageF(HOST_MESSAGE, "%s...", platform->GetConfigFile());
}
else
{
+ platform->MessageF(HOST_MESSAGE, "%s (no configuration file found)...", platform->GetDefaultFile());
configFile = platform->GetDefaultFile();
- platform->MessageF(HOST_MESSAGE, "%s (no configuration file found)... ", configFile);
}
if (gCodes->DoFileMacro(configFile, false))
@@ -87,10 +73,12 @@ void RepRap::Init()
}
processingConfig = false;
+ // Enable network (unless it's disabled)
if (network->IsEnabled())
{
- // EMAC driver will report when it's starting up; no need to do this twice
- network->Enable(); // Need to do this here, as the configuration GCodes may set IP address etc.
+ // Need to do this here, as the configuration GCodes may set IP address etc.
+ platform->Message(HOST_MESSAGE, "Starting network...\n");
+ network->Enable();
}
else
{
@@ -163,7 +151,7 @@ void RepRap::Spin()
{
if (t->DisplayColdExtrudeWarning() && ToolWarningsAllowed())
{
- platform->MessageF(GENERIC_MESSAGE, "Warning: Tool %d was not driven because its temperatures were not high enough or it has a heater fault\n", t->myNumber);
+ platform->MessageF(GENERIC_MESSAGE, "Warning: Tool %d was not driven because its heater temperatures were not high enough or it has a heater fault\n", t->myNumber);
}
}
@@ -208,12 +196,12 @@ void RepRap::Diagnostics()
void RepRap::EmergencyStop()
{
stopped = true;
- platform->SetAtxPower(false); // turn off the ATX power if we can
- //platform->DisableInterrupts();
+ // Do not turn off ATX power here. If the nozzles are still hot, don't risk melting any surrounding parts...
+ //platform->SetAtxPower(false);
Tool* tool = toolList;
- while(tool)
+ while (tool != nullptr)
{
tool->Standby();
tool = tool->Next();
@@ -294,7 +282,8 @@ void RepRap::AddTool(Tool* tool)
}
tool->next = *t;
*t = tool;
- tool->UpdateExtruderAndHeaterCount(activeExtruders, activeHeaters);
+ tool->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters);
+ platform->UpdateConfiguredHeaters();
}
void RepRap::DeleteTool(Tool* tool)
@@ -331,11 +320,12 @@ void RepRap::DeleteTool(Tool* tool)
Tool::Delete(tool);
// Update the number of active heaters and extruder drives
- activeExtruders = activeHeaters = 0;
+ activeExtruders = activeToolHeaters = 0;
for (Tool *t = toolList; t != nullptr; t = t->Next())
{
- t->UpdateExtruderAndHeaterCount(activeExtruders, activeHeaters);
+ t->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters);
}
+ platform->UpdateConfiguredHeaters();
}
void RepRap::SelectTool(int toolNumber)
@@ -370,7 +360,7 @@ void RepRap::PrintTool(int toolNumber, StringRef& reply) const
}
else
{
- reply.copy("Attempt to print details of non-existent tool.\n");
+ reply.copy("Error: Attempt to print details of non-existent tool.\n");
}
}
@@ -387,7 +377,7 @@ void RepRap::StandbyTool(int toolNumber)
}
else
{
- platform->MessageF(GENERIC_MESSAGE, "Attempt to standby a non-existent tool: %d.\n", toolNumber);
+ platform->MessageF(GENERIC_MESSAGE, "Error: Attempt to standby a non-existent tool: %d.\n", toolNumber);
}
}
@@ -419,7 +409,7 @@ void RepRap::SetToolVariables(int toolNumber, const float* standbyTemperatures,
}
else
{
- platform->MessageF(GENERIC_MESSAGE, "Attempt to set variables for a non-existent tool: %d.\n", toolNumber);
+ platform->MessageF(GENERIC_MESSAGE, "Error: Attempt to set variables for a non-existent tool: %d.\n", toolNumber);
}
}
@@ -687,7 +677,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
// Current temperatures
ch = '[';
- for (size_t heater = E0_HEATER; heater < GetHeatersInUse(); heater++)
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, heat->GetTemperature(heater));
ch = ',';
@@ -697,7 +687,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
// Active temperatures
response->catf(",\"active\":");
ch = '[';
- for (size_t heater = E0_HEATER; heater < GetHeatersInUse(); heater++)
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, heat->GetActiveTemperature(heater));
ch = ',';
@@ -707,7 +697,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
// Standby temperatures
response->catf(",\"standby\":");
ch = '[';
- for (size_t heater = E0_HEATER; heater < GetHeatersInUse(); heater++)
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, heat->GetStandbyTemperature(heater));
ch = ',';
@@ -717,7 +707,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
// Heater statuses (0=off, 1=standby, 2=active, 3=fault)
response->cat(",\"state\":");
ch = '[';
- for (size_t heater = E0_HEATER; heater < GetHeatersInUse(); heater++)
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%d", ch, static_cast<int>(heat->GetStatus(heater)));
ch = ',';
@@ -1037,39 +1027,76 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
response->printf("{\"status\":\"%c\",\"heaters\":", ch);
// Send the heater actual temperatures
- ch = '[';
- for (size_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
+ const int8_t bedHeater = heat->GetBedHeater();
+ if (bedHeater != -1)
+ {
+ ch = ',';
+ response->catf("[%.1f", heat->GetTemperature(bedHeater));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, heat->GetTemperature(heater));
ch = ',';
}
+ response->cat((ch == '[') ? "[]" : "]");
// Send the heater active temperatures
- response->catf("],\"active\":");
- ch = '[';
- for (size_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
+ response->catf(",\"active\":");
+ if (heat->GetBedHeater() != -1)
+ {
+ ch = ',';
+ response->catf("[%.1f", heat->GetActiveTemperature(heat->GetBedHeater()));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, heat->GetActiveTemperature(heater));
ch = ',';
}
+ response->cat((ch == '[') ? "[]" : "]");
// Send the heater standby temperatures
- response->catf("],\"standby\":");
- ch = '[';
- for (size_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
+ response->catf(",\"standby\":");
+ if (bedHeater != -1)
+ {
+ ch = ',';
+ response->catf("[%.1f", heat->GetStandbyTemperature(bedHeater));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
response->catf("%c%.1f", ch, heat->GetStandbyTemperature(heater));
ch = ',';
}
+ response->cat((ch == '[') ? "[]" : "]");
// Send the heater statuses (0=off, 1=standby, 2=active)
- response->cat("],\"hstat\":");
- ch = '[';
- for (size_t heater = 0; heater < reprap.GetHeatersInUse(); heater++)
+ response->cat(",\"hstat\":");
+ if (bedHeater != -1)
+ {
+ ch = ',';
+ response->catf("[%d", static_cast<int>(heat->GetStatus(bedHeater)));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++)
{
- response->catf("%c%d", ch, (int)heat->GetStatus(heater));
+ response->catf("%c%d", ch, static_cast<int>(heat->GetStatus(heater)));
ch = ',';
}
+ response->cat((ch == '[') ? "[]" : "]");
// Send XYZ positions
float liveCoordinates[DRIVES];
@@ -1083,7 +1110,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
liveCoordinates[i] += offset[i];
}
}
- response->catf("],\"pos\":"); // announce the XYZ position
+ response->catf(",\"pos\":"); // announce the XYZ position
ch = '[';
for (size_t drive = 0; drive < AXES; drive++)
{
@@ -1133,8 +1160,9 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
// Send the fan0 settings (for PanelDue firmware 1.13)
response->catf(",\"fanPercent\":[%.02f,%.02f]", platform->GetFanValue(0) * 100.0, platform->GetFanValue(1) * 100.0);
+
// Send fan RPM value
- response->catf(",\"fanRPM\":%u", (unsigned int)platform->GetFanRPM());
+ response->catf(",\"fanRPM\":%u", static_cast<unsigned int>(platform->GetFanRPM()));
// Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false.
if (type != 0)
@@ -1302,6 +1330,7 @@ void RepRap::SetMessage(const char *msg)
char RepRap::GetStatusCharacter() const
{
return (processingConfig) ? 'C' // Reading the configuration file
+ : (gCodes->IsFlashing()) ? 'F' // Flashing a new firmware binary
: (IsStopped()) ? 'H' // Halted
: (gCodes->IsPausing()) ? 'D' // Pausing / Decelerating
: (gCodes->IsResuming()) ? 'R' // Resuming
diff --git a/src/Reprap.h b/src/Reprap.h
index dc70bc4e..587d5161 100644
--- a/src/Reprap.h
+++ b/src/Reprap.h
@@ -37,7 +37,6 @@ public:
void Init();
void Spin();
void Exit();
- void Interrupt();
void Diagnostics();
void Timing();
@@ -84,7 +83,7 @@ public:
bool IsStopped() const;
uint16_t GetExtrudersInUse() const;
- uint16_t GetHeatersInUse() const;
+ uint16_t GetToolHeatersInUse() const;
OutputBuffer *GetStatusResponse(uint8_t type, ResponseSource source);
OutputBuffer *GetConfigResponse();
@@ -116,7 +115,7 @@ private:
float lastToolWarningTime;
uint16_t activeExtruders;
- uint16_t activeHeaters;
+ uint16_t activeToolHeaters;
uint16_t ticksInSpinState;
Module spinningModule;
@@ -150,8 +149,7 @@ inline Module RepRap::GetSpinningModule() const { return spinningModule; }
inline Tool* RepRap::GetCurrentTool() const { return currentTool; }
inline uint16_t RepRap::GetExtrudersInUse() const { return activeExtruders; }
-inline uint16_t RepRap::GetHeatersInUse() const { return activeHeaters; }
-inline void RepRap::Interrupt() { move->Interrupt(); }
+inline uint16_t RepRap::GetToolHeatersInUse() const { return activeToolHeaters; }
inline bool RepRap::IsStopped() const { return stopped; }
inline uint16_t RepRap::GetTicksInSpinState() const { return ticksInSpinState; }
diff --git a/src/Webserver.cpp b/src/Webserver.cpp
index f71db6e1..359f8566 100644
--- a/src/Webserver.cpp
+++ b/src/Webserver.cpp
@@ -63,24 +63,6 @@
a JSON response with the variable "err" will be returned, which will be 0 if the job
has finished without problems, it will be set to 1 otherwise.
- rr_upload_begin?name=xxx
- Indicates that we wish to upload the specified file. xxx is the filename relative
- to the root of the SD card. The directory component of the filename must already
- exist. Returns variables ubuff (= max upload data we can accept in the next message)
- and err (= 0 if the file was created successfully, nonzero if there was an error).
-
- rr_upload_data?data=xxx
- Provides a data block for the file upload. Returns the samwe variables as rr_upload_begin,
- except that err is only zero if the file was successfully created and there has not been
- a file write error yet. This response is returned before attempting to write this data block.
-
- rr_upload_end
- Indicates that we have finished sending upload data. The server closes the file and reports
- the overall status in err. It may also return ubuff again.
-
- rr_upload_cancel
- Indicates that the user wishes to cancel the current upload. Returns err and ubuff.
-
rr_delete?name=xxx
Delete file xxx. Returns err (zero if successful).
@@ -120,8 +102,7 @@ Webserver::Webserver(Platform* p, Network *n) : platform(p), network(n), webserv
void Webserver::Init()
{
// initialise the webserver class
- lastTime = platform->Time();
- longWait = lastTime;
+ longWait = platform->Time();
webserverActive = true;
// initialise all protocol handlers
@@ -201,18 +182,14 @@ void Webserver::Spin()
// Check for fast uploads
else if (interpreter->DoingFastUpload())
{
- if (!interpreter->DoFastUpload(transaction))
- {
- // Ensure this connection won't block everything if anything goes wrong
- readingConnection = nullptr;
- }
+ interpreter->DoFastUpload(transaction);
}
// Check if we need to send data to a Telnet client
else if (interpreter == telnetInterpreter && telnetInterpreter->HasDataToSend())
{
telnetInterpreter->SendGCodeReply(transaction);
}
- // Process other messages (unless this is an HTTP request which may need special treatment)
+ // Process other messages (if we can)
else if (interpreter != httpInterpreter || httpInterpreter->IsReady())
{
for(size_t i = 0; i < 500; i++)
@@ -426,38 +403,37 @@ void ProtocolInterpreter::CancelUpload()
fileBeingUploaded.Close(); // cancel any pending file upload
if (strlen(filenameBeingUploaded) != 0)
{
- platform->GetMassStorage()->Delete("0:/", filenameBeingUploaded);
+ platform->GetMassStorage()->Delete(FS_PREFIX, filenameBeingUploaded);
}
}
filenameBeingUploaded[0] = 0;
uploadState = notUploading;
}
-bool ProtocolInterpreter::DoFastUpload(NetworkTransaction *transaction)
+void ProtocolInterpreter::DoFastUpload(NetworkTransaction *transaction)
{
- if (IsUploading())
+ const char *buffer;
+ unsigned int len;
+ if (transaction->ReadBuffer(buffer, len))
{
- char *buffer;
- unsigned int len;
- if (transaction->ReadBuffer(buffer, len))
+ // Writing data usually takes a while, so keep LwIP running while this is being done
+ network->Unlock();
+ if (!fileBeingUploaded.Write(buffer, len))
{
- // Write uploaded data immediately to the file
- if (!fileBeingUploaded.Write(buffer, len))
- {
- platform->Message(HOST_MESSAGE, "Could not write upload data!\n");
- uploadState = uploadError;
- }
- transaction->Discard();
+ platform->Message(HOST_MESSAGE, "Could not write upload data!\n");
+ uploadState = uploadError;
+
+ while (!network->Lock());
+ transaction->Commit(false);
+ return;
}
+ while (!network->Lock());
}
- else if (transaction->DataLength() > 0)
+
+ if (!transaction->HasMoreDataToRead())
{
- platform->Message(HOST_MESSAGE, "Webserver: Closing invalid data connection\n");
- transaction->Commit(false);
- return false;
+ transaction->Discard();
}
-
- return true;
}
void ProtocolInterpreter::FinishUpload(uint32_t fileLength)
@@ -486,7 +462,7 @@ void ProtocolInterpreter::FinishUpload(uint32_t fileLength)
// Delete file if an error has occurred
if (uploadState == uploadError && strlen(filenameBeingUploaded) != 0)
{
- platform->GetMassStorage()->Delete("0:/", filenameBeingUploaded);
+ platform->GetMassStorage()->Delete(FS_PREFIX, filenameBeingUploaded);
}
filenameBeingUploaded[0] = 0;
}
@@ -506,9 +482,8 @@ Webserver::HttpInterpreter::HttpInterpreter(Platform *p, Webserver *ws, Network
{
gcodeReadIndex = gcodeWriteIndex = 0;
gcodeReply = new OutputStack();
- uploadingTextData = false;
processingDeferredRequest = false;
- numContinuationBytes = seq = 0;
+ seq = 0;
}
void Webserver::HttpInterpreter::Diagnostics()
@@ -518,32 +493,44 @@ void Webserver::HttpInterpreter::Diagnostics()
// File Uploads
-bool Webserver::HttpInterpreter::DoFastUpload(NetworkTransaction *transaction)
+bool Webserver::HttpInterpreter::DoingFastUpload() const
{
- // Attempt to write one pbuf entry in one go
- if (IsUploading())
+ uint32_t remoteIP = network->GetTransaction()->GetRemoteIP();
+ uint16_t remotePort = network->GetTransaction()->GetRemotePort();
+ for(size_t i = 0; i < numSessions; i++)
{
- char *buffer;
- unsigned int len;
- if (transaction->ReadBuffer(buffer, len))
- {
- WriteUploadedData(buffer, len);
- uploadedBytes += len;
- }
- else
+ if (sessions[i].ip == remoteIP && sessions[i].isPostUploading)
{
- transaction->Discard();
+ // There is only one session per IP address...
+ return (sessions[i].postPort == remotePort);
}
}
- else if (transaction->DataLength() > 0)
+ return false;
+}
+
+void Webserver::HttpInterpreter::DoFastUpload(NetworkTransaction *transaction)
+{
+ // Write some data on the SD card
+ const char *buffer;
+ unsigned int len;
+ if (transaction->ReadBuffer(buffer, len))
{
- platform->Message(HOST_MESSAGE, "Webserver: Closing invalid data connection\n");
- transaction->Commit(false);
- return false;
+ network->Unlock();
+ if (!fileBeingUploaded.Write(buffer, len))
+ {
+ platform->Message(HOST_MESSAGE, "Could not write upload data!\n");
+ uploadState = uploadError;
+
+ while (!network->Lock());
+ SendJsonResponse("upload");
+ return;
+ }
+ uploadedBytes += len;
+ while (!network->Lock());
}
// See if the upload has finished
- if (uploadState != uploadOK || uploadedBytes >= postFileLength)
+ if (uploadState == uploadOK && uploadedBytes >= postFileLength)
{
// Reset POST upload state for this client
uint32_t remoteIP = transaction->GetRemoteIP();
@@ -561,75 +548,20 @@ bool Webserver::HttpInterpreter::DoFastUpload(NetworkTransaction *transaction)
SendJsonResponse("upload");
uploadState = notUploading;
}
-
- return (uploadState != uploadError);
-}
-
-bool Webserver::HttpInterpreter::DoingFastUpload() const
-{
- if (state == doingPost)
- {
- // Always finish the current request before checking for fast POST uploads
- return false;
- }
-
- uint32_t remoteIP = network->GetTransaction()->GetRemoteIP();
- uint16_t remotePort = network->GetTransaction()->GetRemotePort();
- for(size_t i = 0; i < numSessions; i++)
+ else if (!transaction->HasMoreDataToRead())
{
- if (sessions[i].ip == remoteIP && sessions[i].isPostUploading)
- {
- return (remotePort == sessions[i].postPort);
- }
- }
- return false;
-}
-
-bool Webserver::HttpInterpreter::StartUpload(FileStore *file)
-{
- numContinuationBytes = 0;
- return ProtocolInterpreter::StartUpload(file);
-}
-
-void Webserver::HttpInterpreter::WriteUploadedData(const char *buffer, unsigned int length)
-{
- // Count the number of UTF8 continuation bytes. We may need it to adjust the expected file length.
- if (uploadingTextData)
- {
- unsigned int bytesToCheck = length;
- const char *data = buffer;
- while (bytesToCheck != 0)
- {
- if ((*data & 0xC0) == 0x80)
- {
- ++numContinuationBytes;
- }
- ++data;
- --bytesToCheck;
- }
- }
-
- // Write uploaded data immediately to the file
- if (!fileBeingUploaded.Write(buffer, length))
- {
- platform->Message(HOST_MESSAGE, "Could not write upload data!\n");
- uploadState = uploadError;
+ transaction->Discard();
}
}
void Webserver::HttpInterpreter::CancelUpload()
{
- CancelUpload(network->GetTransaction()->GetRemoteIP());
-}
-
-void Webserver::HttpInterpreter::CancelUpload(uint32_t remoteIP)
-{
for(size_t i = 0; i < numSessions; i++)
{
- if (sessions[i].ip == remoteIP && sessions[i].isPostUploading)
+ if (sessions[i].isPostUploading)
{
sessions[i].isPostUploading = false;
- sessions[i].lastQueryTime = platform->Time();
+ sessions[i].lastQueryTime = millis();
break;
}
}
@@ -715,6 +647,9 @@ void Webserver::HttpInterpreter::SendConfigFile(NetworkTransaction *transaction)
FileStore *configFile = platform->GetFileStore(platform->GetSysDir(), platform->GetConfigFile(), false);
transaction->Write("HTTP/1.1 200 OK\n");
+ transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
+ transaction->Write("Pragma: no-cache\n");
+ transaction->Write("Expires: 0\n");
transaction->Write("Content-Type: text/plain\n");
transaction->Printf("Content-Length: %u\n", (configFile != nullptr) ? configFile->Length() : 0);
transaction->Write("Connection: close\n\n");
@@ -743,7 +678,7 @@ void Webserver::HttpInterpreter::SendGCodeReply(NetworkTransaction *transaction)
if (reprap.Debug(moduleWebserver))
{
- platform->MessageF(HOST_MESSAGE, "Serving client %d of %d\n", clientsServed, numSessions);
+ platform->MessageF(HOST_MESSAGE, "Sending G-Code reply to client %d of %d (length %u)\n", clientsServed, numSessions, gcodeReply->DataLength());
}
}
@@ -968,43 +903,9 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff
{
response->printf("{\"err\":%d}", (uploadState == uploadOK && uploadedBytes == postFileLength) ? 0 : 1);
}
- else if (StringEquals(request, "upload_begin") && StringEquals(key, "name"))
- {
- FileStore *file = platform->GetFileStore("0:/", value, true);
- if (StartUpload(file))
- {
- strncpy(filenameBeingUploaded, value, ARRAY_SIZE(filenameBeingUploaded));
- filenameBeingUploaded[ARRAY_UPB(filenameBeingUploaded)] = 0;
- uploadingTextData = (numQualKeys < 2 || !StringEquals(qualifiers[1].key, "type") ||
- !StringEquals(qualifiers[1].value, "binary"));
- }
-
- GetJsonUploadResponse(response);
- }
- else if (StringEquals(request, "upload_data") && StringEquals(key, "data"))
- {
- WriteUploadedData(value, valueLength);
- uploadedBytes += valueLength;
-
- GetJsonUploadResponse(response);
- keepOpen = true;
- }
- else if (StringEquals(request, "upload_end") && StringEquals(key, "size"))
- {
- uint32_t fileLength = strtoul(value, nullptr, 10);
- FinishUpload(fileLength);
-
- GetJsonUploadResponse(response);
- uploadState = notUploading;
- }
- else if (StringEquals(request, "upload_cancel"))
- {
- CancelUpload();
- response->copy("{\"err\":0}");
- }
else if (StringEquals(request, "delete") && StringEquals(key, "name"))
{
- bool ok = platform->GetMassStorage()->Delete("0:/", value);
+ bool ok = platform->GetMassStorage()->Delete(FS_PREFIX, value);
response->printf("{\"err\":%d}", (ok) ? 0 : 1);
}
else if (StringEquals(request, "files"))
@@ -1023,15 +924,15 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff
}
else if (StringEquals(request, "fileinfo"))
{
+ // This may take a while before it returns, so allow LwIP to send ACKs while we're waiting for it
+ network->Unlock();
OutputBuffer::Release(response);
- if (reprap.GetPrintMonitor()->GetFileInfoResponse(StringEquals(key, "name") ? value : nullptr, response))
- {
- processingDeferredRequest = false;
- }
- else
+ processingDeferredRequest = !reprap.GetPrintMonitor()->GetFileInfoResponse(StringEquals(key, "name") ? value : nullptr, response);
+ while (!network->Lock()); // This won't block for long
+
+ if (processingDeferredRequest)
{
network->GetTransaction()->Defer();
- processingDeferredRequest = true;
}
}
else if (StringEquals(request, "move"))
@@ -1089,22 +990,9 @@ void Webserver::HttpInterpreter::ResetState()
bool Webserver::HttpInterpreter::NeedMoreData()
{
- if (state == doingPost)
+ if (!IsAuthenticated() && (!numCommandWords || !StringEquals(commandWords[0], "connect")))
{
- // At this stage we've processed the first chunk of a POST upload request. Store the
- // initial payload and reset the HTTP reader again in order to process new requests
- WriteUploadedData(clientMessage + (clientPointer - uploadedBytes), uploadedBytes);
- if (reprap.Debug(moduleWebserver))
- {
- platform->MessageF(HOST_MESSAGE, "Wrote %lu bytes of file\n", uploadedBytes);
- }
- ResetState();
- return false;
- }
- else if (!IsAuthenticated() && (!numCommandWords || !StringEquals(commandWords[0], "connect")))
- {
- // It makes very little sense to allow unknown connections to block our HTTP reader, however
- // connect requests may take up more than one TCP_MSS if a long cookie value is passed.
+ // It makes very little sense to allow unknown connections to block our HTTP reader
ResetState();
return false;
}
@@ -1140,7 +1028,7 @@ void Webserver::HttpInterpreter::ConnectionLost(uint32_t remoteIP, uint16_t remo
{
platform->MessageF(HOST_MESSAGE, "POST upload for '%s' has been cancelled!\n", filenameBeingUploaded);
}
- CancelUpload(remoteIP);
+ CancelUpload();
break;
}
}
@@ -1439,36 +1327,6 @@ bool Webserver::HttpInterpreter::CharFromClient(char c)
}
break;
- case doingPost:
- clientMessage[clientPointer++] = c;
- uploadedBytes++;
-
- if (uploadedBytes == postFileLength)
- {
- WriteUploadedData(clientMessage + (clientPointer - uploadedBytes), uploadedBytes);
- FinishUpload(postFileLength);
-
- SendJsonResponse("upload");
-
- // Reset state
- uint32_t remoteIP = network->GetTransaction()->GetRemoteIP();
- for(size_t i = 0; i < numSessions; i++)
- {
- if (sessions[i].ip == remoteIP && sessions[i].isPostUploading)
- {
- sessions[i].isPostUploading = false;
- sessions[i].lastQueryTime = platform->Time();
- break;
- }
- }
- uploadState = notUploading;
- ResetState();
-
- return true;
- }
-
- break;
-
default:
break;
}
@@ -1554,55 +1412,42 @@ bool Webserver::HttpInterpreter::ProcessMessage()
}
// Start POST file upload
- if (contentLengthFound)
+ if (!contentLengthFound)
{
- FileStore *file = platform->GetFileStore("0:/", qualifiers[0].value, true);
- if (StartUpload(file))
- {
- if (reprap.Debug(moduleWebserver))
- {
- platform->MessageF(HOST_MESSAGE, "Start uploading file %s length %lu\n", qualifiers[0].value, postFileLength);
- }
- // Start new file upload
- uploadingTextData = false;
- uploadedBytes = numContinuationBytes = 0;
+ return RejectMessage("invalid POST upload request");
+ }
- strncpy(filenameBeingUploaded, qualifiers[0].value, ARRAY_SIZE(filenameBeingUploaded));
- filenameBeingUploaded[ARRAY_UPB(filenameBeingUploaded)] = 0;
+ // Start a new file upload
+ FileStore *file = platform->GetFileStore(FS_PREFIX, qualifiers[0].value, true);
+ if (!StartUpload(file))
+ {
+ return RejectMessage("could not start file upload");
+ }
- // Set POST variables only if we actually need to store data
- if (postFileLength > 0)
- {
- uint32_t remoteIP = network->GetTransaction()->GetRemoteIP();
- uint16_t remotePort = network->GetTransaction()->GetRemotePort();
- for(size_t i = 0; i < numSessions; i++)
- {
- if (sessions[i].ip == remoteIP)
- {
- sessions[i].postPort = remotePort;
- sessions[i].isPostUploading = true;
- break;
- }
- }
-
- // Align the client pointer on a 32-bit boundary for HSMCI efficiency.
- // To remain efficient, the browser should send POSTDATA in multiples of 4 bytes.
- clientPointer += 3;
- clientPointer -= reinterpret_cast<size_t>(clientMessage + clientPointer) & 3;
- state = doingPost;
-
- return false;
- }
+ if (reprap.Debug(moduleWebserver))
+ {
+ platform->MessageF(HOST_MESSAGE, "Start uploading file %s length %lu\n", qualifiers[0].value, postFileLength);
+ }
+ uploadedBytes = 0;
- // User has uploaded an empty file - we should have finished here already
- FinishUpload(0);
- SendJsonResponse("upload");
- return true;
+ strncpy(filenameBeingUploaded, qualifiers[0].value, ARRAY_SIZE(filenameBeingUploaded));
+ filenameBeingUploaded[ARRAY_UPB(filenameBeingUploaded)] = 0;
+
+ uint32_t remoteIP = network->GetTransaction()->GetRemoteIP();
+ uint16_t remotePort = network->GetTransaction()->GetRemotePort();
+ for(size_t i = 0; i < numSessions; i++)
+ {
+ if (sessions[i].ip == remoteIP)
+ {
+ sessions[i].postPort = remotePort;
+ sessions[i].isPostUploading = true;
+ break;
}
- return RejectMessage("could not start file upload");
}
+
+ ResetState();
+ return true;
}
- return RejectMessage("invalid POST upload request");
}
return RejectMessage("only rr_upload is supported for POST requests");
}
@@ -1632,7 +1477,7 @@ bool Webserver::HttpInterpreter::Authenticate()
if (numSessions < maxHttpSessions)
{
sessions[numSessions].ip = network->GetTransaction()->GetRemoteIP();
- sessions[numSessions].lastQueryTime = platform->Time();
+ sessions[numSessions].lastQueryTime = millis();
sessions[numSessions].isPostUploading = false;
numSessions++;
return true;
@@ -1660,7 +1505,7 @@ void Webserver::HttpInterpreter::UpdateAuthentication()
{
if (sessions[i].ip == remoteIP)
{
- sessions[i].lastQueryTime = platform->Time();
+ sessions[i].lastQueryTime = millis();
break;
}
}
@@ -1686,12 +1531,12 @@ void Webserver::HttpInterpreter::RemoveAuthentication()
void Webserver::HttpInterpreter::CheckSessions()
{
// Check if any HTTP session can be purged
- const float time = platform->Time();
+ const uint32_t now = millis();
for (int i = numSessions - 1; i >=0 ; i--)
{
- if (!sessions[i].isPostUploading && (time - sessions[i].lastQueryTime) > httpSessionTimeout)
+ if (!sessions[i].isPostUploading && (now - sessions[i].lastQueryTime) > httpSessionTimeout)
{
- for (size_t k = i + 1; k < numSessions; ++k)
+ for (size_t k = i + 1; k < numSessions; k++)
{
memcpy(&sessions[k - 1], &sessions[k], sizeof(HttpSession));
}
@@ -1856,6 +1701,7 @@ void Webserver::HttpInterpreter::HandleGCodeReply(const char *reply)
}
buffer->cat(reply);
+ clientsServed = 0;
seq++;
}
}
@@ -2132,7 +1978,7 @@ void Webserver::FtpInterpreter::ProcessLine()
rand();
uint16_t pasv_port = random(1024, 65535);
network->OpenDataPort(pasv_port);
- portOpenTime = platform->Time();
+ portOpenTime = millis();
state = waitingForPasvPort;
/* send FTP response */
@@ -2248,7 +2094,7 @@ void Webserver::FtpInterpreter::ProcessLine()
break;
case waitingForPasvPort:
- if (!reprap.Debug(moduleWebserver) && platform->Time() - portOpenTime > ftpPasvPortTimeout)
+ if (!reprap.Debug(moduleWebserver) && millis() - portOpenTime > ftpPasvPortTimeout)
{
SendReply(425, "Failed to establish connection.");
@@ -2550,7 +2396,7 @@ void Webserver::TelnetInterpreter::ConnectionEstablished()
return;
}
state = justConnected;
- connectTime = platform->Time();
+ connectTime = millis();
// Check whether we need a password to log in
if (reprap.NoPasswordSet())
@@ -2596,7 +2442,7 @@ bool Webserver::TelnetInterpreter::CharFromClient(char c)
state = authenticating;
}
- if (platform->Time() - connectTime < telnetSetupDuration)
+ if (millis() - connectTime < telnetSetupDuration)
{
network->GetTransaction()->Discard();
return true;
@@ -2636,7 +2482,7 @@ bool Webserver::TelnetInterpreter::CharFromClient(char c)
void Webserver::TelnetInterpreter::ResetState()
{
state = idle;
- connectTime = 0.0;
+ connectTime = 0;
clientPointer = 0;
}
diff --git a/src/Webserver.h b/src/Webserver.h
index b03b2634..0d529c3f 100644
--- a/src/Webserver.h
+++ b/src/Webserver.h
@@ -39,26 +39,26 @@ const uint16_t gcodeBufferLength = 512; // size of our gcode ring buffer, pref
const uint16_t webUploadBufferSize = 2300; // maximum size of HTTP GET upload packets (webMessageLength - 700)
const uint16_t webMessageLength = 3000; // maximum length of the web message we accept after decoding
-const size_t minHttpResponseSize = 1024; // minimum number of bytes required for an HTTP response
+const size_t minHttpResponseSize = 768; // minimum number of bytes required for an HTTP response
const size_t maxCommandWords = 4; // max number of space-separated words in the command
const size_t maxQualKeys = 5; // max number of key/value pairs in the qualifier
const size_t maxHeaders = 16; // max number of key/value pairs in the headers
const size_t maxHttpSessions = 8; // maximum number of simultaneous HTTP sessions
-const float httpSessionTimeout = 8.0; // HTTP session timeout in seconds
+const uint32_t httpSessionTimeout = 8000; // HTTP session timeout in milliseconds
/* FTP */
const uint16_t ftpResponseLength = 128; // maximum FTP response length
const uint16_t ftpFileListLineLength = 256; // maximum length for one FTP file listing line
const uint16_t ftpMessageLength = 128; // maximum line length for incoming FTP commands
-const float ftpPasvPortTimeout = 10.0; // maximum time to wait for an FTP data connection
+const uint32_t ftpPasvPortTimeout = 10000; // maximum time to wait for an FTP data connection in milliseconds
/* Telnet */
const uint16_t telnetMessageLength = 128; // maximum line length for incoming Telnet commands
-const float telnetSetupDuration = 4.0; // ignore the first Telnet request within this duration
+const uint32_t telnetSetupDuration = 4000; // ignore the first Telnet request within this duration (in ms)
class Webserver;
@@ -85,31 +85,31 @@ class ProtocolInterpreter
virtual void ResetState() = 0;
virtual bool NeedMoreData();
- virtual bool DoFastUpload(NetworkTransaction *transaction);
virtual bool DoingFastUpload() const;
- virtual void CancelUpload();
+ virtual void DoFastUpload(NetworkTransaction *transaction);
+ virtual void CancelUpload();
protected:
- Platform *platform;
- Webserver *webserver;
- Network *network;
+ Platform *platform;
+ Webserver *webserver;
+ Network *network;
- // Information for file uploading
- enum UploadState
- {
+ // Information for file uploading
+ enum UploadState
+ {
notUploading, // no upload in progress
uploadOK, // upload in progress, no error so far
uploadError // upload in progress but had error
- };
+ };
- UploadState uploadState;
- FileData fileBeingUploaded;
- char filenameBeingUploaded[FILENAME_LENGTH];
+ UploadState uploadState;
+ FileData fileBeingUploaded;
+ char filenameBeingUploaded[FILENAME_LENGTH];
- virtual bool StartUpload(FileStore *file);
+ virtual bool StartUpload(FileStore *file);
bool IsUploading() const;
- virtual void FinishUpload(uint32_t fileLength);
+ virtual void FinishUpload(uint32_t fileLength);
};
class Webserver
@@ -149,10 +149,9 @@ class Webserver
void ResetState() override;
bool NeedMoreData() override;
- bool DoFastUpload(NetworkTransaction *transaction) override;
bool DoingFastUpload() const override;
+ void DoFastUpload(NetworkTransaction *transaction) override;
void CancelUpload() override;
- void CancelUpload(uint32_t remoteIP);
void ResetSessions();
void CheckSessions();
@@ -184,8 +183,7 @@ class Webserver
doingHeaderKey, // receiving a header key
expectingHeaderValue, // expecting a header value
doingHeaderValue, // receiving a header value
- doingHeaderContinuation, // received a newline after a header value
- doingPost // receiving post data
+ doingHeaderContinuation // received a newline after a header value
};
HttpState state;
@@ -220,7 +218,7 @@ class Webserver
struct HttpSession
{
uint32_t ip;
- float lastQueryTime;
+ uint32_t lastQueryTime;
bool isPostUploading;
uint16_t postPort;
};
@@ -249,14 +247,7 @@ class Webserver
protected:
bool processingDeferredRequest; // it's no good idea to parse 128kB of text in one go...
- bool uploadingTextData; // do we need to count UTF-8 continuation bytes?
- uint32_t numContinuationBytes; // number of UTF-8 continuation bytes we have received
-
uint32_t postFileLength, uploadedBytes; // how many POST bytes do we expect and how many have already been written?
-
- bool StartUpload(FileStore *file) override;
- void WriteUploadedData(const char* buffer, unsigned int length);
- void FinishUpload(uint32_t fileLength) override;
};
HttpInterpreter *httpInterpreter;
@@ -294,7 +285,7 @@ class Webserver
char filename[FILENAME_LENGTH];
char currentDir[FILENAME_LENGTH];
- float portOpenTime;
+ uint32_t portOpenTime;
void ProcessLine();
void SendReply(int code, const char *message, bool keepConnection = true);
@@ -337,7 +328,7 @@ class Webserver
};
TelnetState state;
uint8_t connectedClients;
- float connectTime;
+ uint32_t connectTime;
char clientMessage[telnetMessageLength];
uint16_t clientPointer;
@@ -365,7 +356,6 @@ class Webserver
bool webserverActive;
const ConnectionState *readingConnection;
- float lastTime;
float longWait;
};
@@ -375,7 +365,6 @@ inline bool ProtocolInterpreter::IsUploading() const { return uploadState != not
inline uint32_t Webserver::GetReplySeq() const { return httpInterpreter->GetReplySeq(); }
-inline void Webserver::HttpInterpreter::FinishUpload(uint32_t fileLength) { ProtocolInterpreter::FinishUpload(fileLength + numContinuationBytes); }
inline uint16_t Webserver::HttpInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
inline bool Webserver::HttpInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
inline uint32_t Webserver::HttpInterpreter::GetReplySeq() const { return seq; }